@imgly/editor-react-native 1.73.0 → 1.74.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/IMGLYEditorModule.podspec +0 -21
  2. package/android/build.gradle +1 -5
  3. package/android/src/main/assets/scene/apparel.scene +1 -0
  4. package/android/src/main/assets/scene/design.scene +1 -0
  5. package/android/src/main/assets/scene/postcard.scene +1 -0
  6. package/android/src/main/assets/scene/video.scene +1 -0
  7. package/android/src/main/kotlin/ly/img/editor/configuration/apparel/ApparelConfigurationBuilder.kt +113 -0
  8. package/android/src/main/kotlin/ly/img/editor/configuration/apparel/EditorActivity.kt +37 -0
  9. package/android/src/main/kotlin/ly/img/editor/configuration/apparel/callback/OnCreate.kt +190 -0
  10. package/android/src/main/kotlin/ly/img/editor/configuration/apparel/callback/OnExport.kt +82 -0
  11. package/android/src/main/kotlin/ly/img/editor/configuration/apparel/callback/OnLoaded.kt +88 -0
  12. package/android/src/main/kotlin/ly/img/editor/configuration/apparel/component/CanvasMenu.kt +45 -0
  13. package/android/src/main/kotlin/ly/img/editor/configuration/apparel/component/Dock.kt +37 -0
  14. package/android/src/main/kotlin/ly/img/editor/configuration/apparel/component/InspectorBar.kt +52 -0
  15. package/android/src/main/kotlin/ly/img/editor/configuration/apparel/component/NavigationBar.kt +45 -0
  16. package/android/src/main/kotlin/ly/img/editor/configuration/apparel/component/Overlay.kt +15 -0
  17. package/android/src/main/kotlin/ly/img/editor/configuration/design/DesignConfigurationBuilder.kt +106 -0
  18. package/android/src/main/kotlin/ly/img/editor/configuration/design/EditorActivity.kt +37 -0
  19. package/android/src/main/kotlin/ly/img/editor/configuration/design/callback/OnCreate.kt +162 -0
  20. package/android/src/main/kotlin/ly/img/editor/configuration/design/callback/OnExport.kt +82 -0
  21. package/android/src/main/kotlin/ly/img/editor/configuration/design/callback/OnLoaded.kt +10 -0
  22. package/android/src/main/kotlin/ly/img/editor/configuration/design/component/CanvasMenu.kt +45 -0
  23. package/android/src/main/kotlin/ly/img/editor/configuration/design/component/Dock.kt +35 -0
  24. package/android/src/main/kotlin/ly/img/editor/configuration/design/component/InspectorBar.kt +52 -0
  25. package/android/src/main/kotlin/ly/img/editor/configuration/design/component/NavigationBar.kt +45 -0
  26. package/android/src/main/kotlin/ly/img/editor/configuration/design/component/Overlay.kt +15 -0
  27. package/android/src/main/kotlin/ly/img/editor/configuration/photo/EditorActivity.kt +37 -0
  28. package/android/src/main/kotlin/ly/img/editor/configuration/photo/PhotoConfigurationBuilder.kt +106 -0
  29. package/android/src/main/kotlin/ly/img/editor/configuration/photo/callback/OnCreate.kt +143 -0
  30. package/android/src/main/kotlin/ly/img/editor/configuration/photo/callback/OnExport.kt +82 -0
  31. package/android/src/main/kotlin/ly/img/editor/configuration/photo/callback/OnLoaded.kt +49 -0
  32. package/android/src/main/kotlin/ly/img/editor/configuration/photo/component/CanvasMenu.kt +45 -0
  33. package/android/src/main/kotlin/ly/img/editor/configuration/photo/component/Dock.kt +43 -0
  34. package/android/src/main/kotlin/ly/img/editor/configuration/photo/component/InspectorBar.kt +52 -0
  35. package/android/src/main/kotlin/ly/img/editor/configuration/photo/component/NavigationBar.kt +45 -0
  36. package/android/src/main/kotlin/ly/img/editor/configuration/photo/component/Overlay.kt +15 -0
  37. package/android/src/main/kotlin/ly/img/editor/configuration/postcard/EditorActivity.kt +37 -0
  38. package/android/src/main/kotlin/ly/img/editor/configuration/postcard/PostcardConfigurationBuilder.kt +106 -0
  39. package/android/src/main/kotlin/ly/img/editor/configuration/postcard/callback/OnCreate.kt +149 -0
  40. package/android/src/main/kotlin/ly/img/editor/configuration/postcard/callback/OnExport.kt +82 -0
  41. package/android/src/main/kotlin/ly/img/editor/configuration/postcard/callback/OnLoaded.kt +58 -0
  42. package/android/src/main/kotlin/ly/img/editor/configuration/postcard/component/CanvasMenu.kt +45 -0
  43. package/android/src/main/kotlin/ly/img/editor/configuration/postcard/component/Dock.kt +243 -0
  44. package/android/src/main/kotlin/ly/img/editor/configuration/postcard/component/InspectorBar.kt +52 -0
  45. package/android/src/main/kotlin/ly/img/editor/configuration/postcard/component/NavigationBar.kt +76 -0
  46. package/android/src/main/kotlin/ly/img/editor/configuration/postcard/component/Overlay.kt +15 -0
  47. package/android/src/main/kotlin/ly/img/editor/configuration/postcard/ext/Engine.kt +107 -0
  48. package/android/src/main/kotlin/ly/img/editor/configuration/video/EditorActivity.kt +37 -0
  49. package/android/src/main/kotlin/ly/img/editor/configuration/video/VideoConfigurationBuilder.kt +135 -0
  50. package/android/src/main/kotlin/ly/img/editor/configuration/video/callback/OnCreate.kt +191 -0
  51. package/android/src/main/kotlin/ly/img/editor/configuration/video/callback/OnExport.kt +98 -0
  52. package/android/src/main/kotlin/ly/img/editor/configuration/video/callback/OnLoaded.kt +20 -0
  53. package/android/src/main/kotlin/ly/img/editor/configuration/video/component/BottomPanel.kt +13 -0
  54. package/android/src/main/kotlin/ly/img/editor/configuration/video/component/CanvasMenu.kt +45 -0
  55. package/android/src/main/kotlin/ly/img/editor/configuration/video/component/Dock.kt +71 -0
  56. package/android/src/main/kotlin/ly/img/editor/configuration/video/component/InspectorBar.kt +68 -0
  57. package/android/src/main/kotlin/ly/img/editor/configuration/video/component/NavigationBar.kt +57 -0
  58. package/android/src/main/kotlin/ly/img/editor/configuration/video/component/Overlay.kt +421 -0
  59. package/android/src/main/kotlin/ly/img/editor/configuration/video/iconPack/CheckCircleOutline.kt +79 -0
  60. package/android/src/main/kotlin/ly/img/editor/configuration/video/iconPack/ErrorOutline.kt +92 -0
  61. package/android/src/main/kotlin/ly/img/editor/configuration/video/iconPack/IconPack.kt +14 -0
  62. package/android/src/main/kotlin/ly/img/editor/configuration/video/model/ExportStatus.kt +39 -0
  63. package/ios/IMGLYEditorModule.swift +9 -1
  64. package/ios/StarterKits/Apparel/ApparelEditorConfiguration.swift +46 -0
  65. package/ios/StarterKits/Apparel/ApparelEditorStarterKit.swift +30 -0
  66. package/ios/StarterKits/Apparel/callbacks/OnChanged+Apparel.swift +116 -0
  67. package/ios/StarterKits/Apparel/callbacks/OnCreate+Apparel.swift +197 -0
  68. package/ios/StarterKits/Apparel/callbacks/OnExport+Apparel.swift +29 -0
  69. package/ios/StarterKits/Apparel/components/CanvasMenu+Apparel.swift +22 -0
  70. package/ios/StarterKits/Apparel/components/Dock+Apparel.swift +19 -0
  71. package/ios/StarterKits/Apparel/components/InspectorBar+Apparel.swift +35 -0
  72. package/ios/StarterKits/Apparel/components/NavigationBar+Apparel.swift +26 -0
  73. package/ios/StarterKits/Design/DesignEditorConfiguration.swift +43 -0
  74. package/ios/StarterKits/Design/DesignEditorStarterKit.swift +30 -0
  75. package/ios/StarterKits/Design/callbacks/OnCreate+Design.swift +131 -0
  76. package/ios/StarterKits/Design/callbacks/OnExport+Design.swift +30 -0
  77. package/ios/StarterKits/Design/components/CanvasMenu+Design.swift +22 -0
  78. package/ios/StarterKits/Design/components/Dock+Design.swift +23 -0
  79. package/ios/StarterKits/Design/components/InspectorBar+Design.swift +35 -0
  80. package/ios/StarterKits/Design/components/NavigationBar+Design.swift +24 -0
  81. package/ios/StarterKits/Photo/PhotoEditorConfiguration.swift +48 -0
  82. package/ios/StarterKits/Photo/PhotoEditorStarterKit.swift +30 -0
  83. package/ios/StarterKits/Photo/callbacks/OnChanged+Photo.swift +25 -0
  84. package/ios/StarterKits/Photo/callbacks/OnCreate+Photo.swift +138 -0
  85. package/ios/StarterKits/Photo/callbacks/OnExport+Photo.swift +29 -0
  86. package/ios/StarterKits/Photo/callbacks/OnLoaded+Photo.swift +18 -0
  87. package/ios/StarterKits/Photo/components/CanvasMenu+Photo.swift +22 -0
  88. package/ios/StarterKits/Photo/components/Dock+Photo.swift +23 -0
  89. package/ios/StarterKits/Photo/components/InspectorBar+Photo.swift +39 -0
  90. package/ios/StarterKits/Photo/components/NavigationBar+Photo.swift +24 -0
  91. package/ios/StarterKits/Photo/examples/ExampleForceCrop.swift +30 -0
  92. package/ios/StarterKits/Postcard/PostcardEditorConfiguration.swift +45 -0
  93. package/ios/StarterKits/Postcard/PostcardEditorStarterKit.swift +30 -0
  94. package/ios/StarterKits/Postcard/callbacks/OnChanged+Postcard.swift +92 -0
  95. package/ios/StarterKits/Postcard/callbacks/OnCreate+Postcard.swift +127 -0
  96. package/ios/StarterKits/Postcard/callbacks/OnExport+Postcard.swift +29 -0
  97. package/ios/StarterKits/Postcard/components/CanvasMenu+Postcard.swift +22 -0
  98. package/ios/StarterKits/Postcard/components/Dock+Postcard.swift +34 -0
  99. package/ios/StarterKits/Postcard/components/DockButtons+Postcard.swift +129 -0
  100. package/ios/StarterKits/Postcard/components/InspectorBar+Postcard.swift +35 -0
  101. package/ios/StarterKits/Postcard/components/NavigationBar+Postcard.swift +38 -0
  102. package/ios/StarterKits/Video/VideoEditorConfiguration.swift +54 -0
  103. package/ios/StarterKits/Video/VideoEditorStarterKit.swift +30 -0
  104. package/ios/StarterKits/Video/callbacks/OnCreate+Video.swift +137 -0
  105. package/ios/StarterKits/Video/callbacks/OnExport+Video.swift +46 -0
  106. package/ios/StarterKits/Video/components/BottomPanel+Video.swift +16 -0
  107. package/ios/StarterKits/Video/components/CanvasMenu+Video.swift +22 -0
  108. package/ios/StarterKits/Video/components/Dock+Video.swift +30 -0
  109. package/ios/StarterKits/Video/components/InspectorBar+Video.swift +43 -0
  110. package/ios/StarterKits/Video/components/NavigationBar+Video.swift +32 -0
  111. package/ios/StarterKits/Video/examples/ExampleVideoDurationConstraints.swift +23 -0
  112. package/ios/StarterKits/apparel-ui-b-empty.scene +1 -0
  113. package/ios/StarterKits/design-ui-empty.scene +1 -0
  114. package/ios/StarterKits/photo-ui-empty.png +0 -0
  115. package/ios/StarterKits/postcard-empty.scene +1 -0
  116. package/ios/StarterKits/video-empty.scene +1 -0
  117. package/package.json +1 -1
  118. package/src/NativeIMGLYEditor.ts +1 -1
@@ -0,0 +1,149 @@
1
+ @file:Suppress("UnusedReceiverParameter")
2
+
3
+ package ly.img.editor.configuration.postcard.callback
4
+
5
+ import androidx.core.net.toUri
6
+ import kotlinx.coroutines.coroutineScope
7
+ import kotlinx.coroutines.launch
8
+ import ly.img.editor.configuration.postcard.PostcardConfigurationBuilder
9
+ import ly.img.editor.core.library.data.AssetSourceType
10
+ import ly.img.editor.core.library.data.SystemGalleryAssetSource
11
+ import ly.img.editor.core.library.data.SystemGalleryPermission
12
+ import ly.img.editor.core.library.data.TextAssetSource
13
+ import ly.img.editor.core.library.data.TypefaceProvider
14
+ import ly.img.engine.DefaultAssetSource
15
+ import ly.img.engine.DemoAssetSource
16
+ import ly.img.engine.GlobalScope
17
+ import ly.img.engine.populateAssetSource
18
+
19
+ /**
20
+ * The callback that is invoked when the editor is created.
21
+ */
22
+ suspend fun PostcardConfigurationBuilder.onCreate(
23
+ preCreateScene: suspend PostcardConfigurationBuilder.() -> Unit = {
24
+ onPreCreateScene()
25
+ },
26
+ createScene: suspend PostcardConfigurationBuilder.() -> Unit = {
27
+ onCreateScene()
28
+ },
29
+ loadAssetSources: suspend PostcardConfigurationBuilder.() -> Unit = {
30
+ onLoadAssetSources()
31
+ },
32
+ postCreateScene: suspend PostcardConfigurationBuilder.() -> Unit = {
33
+ onPostCreateScene()
34
+ },
35
+ finally: suspend PostcardConfigurationBuilder.() -> Unit = {
36
+ onCreateFinally()
37
+ },
38
+ ) {
39
+ try {
40
+ preCreateScene()
41
+ createScene()
42
+ loadAssetSources()
43
+ postCreateScene()
44
+ } finally {
45
+ finally()
46
+ }
47
+ }
48
+
49
+ /**
50
+ * The callback that is invoked before the scene is created.
51
+ */
52
+ fun PostcardConfigurationBuilder.onPreCreateScene() {
53
+ showLoading = true
54
+ editorContext.engine.editor.setGlobalScope("editor/add", GlobalScope.DEFER)
55
+ }
56
+
57
+ /**
58
+ * The callback that is responsible for creating the scene.
59
+ */
60
+ // highlight-starter-kit-on-create-scene
61
+ suspend fun PostcardConfigurationBuilder.onCreateScene() {
62
+ getOrLoadScene(sceneUri = "file:///android_asset/scene/postcard.scene".toUri())
63
+ }
64
+ // highlight-starter-kit-on-create-scene
65
+
66
+ /**
67
+ * The callback that loads all the required assets sources.
68
+ */
69
+ // highlight-starter-kit-on-load-asset-sources
70
+ suspend fun PostcardConfigurationBuilder.onLoadAssetSources() {
71
+ // Load asset sources in parallel from content.json files
72
+ coroutineScope {
73
+ listOf(
74
+ DefaultAssetSource.STICKER.key,
75
+ DefaultAssetSource.VECTOR_PATH.key,
76
+ DefaultAssetSource.FILTER_LUT.key,
77
+ DefaultAssetSource.FILTER_DUO_TONE.key,
78
+ DefaultAssetSource.CROP_PRESETS.key,
79
+ DefaultAssetSource.EFFECT.key,
80
+ DefaultAssetSource.BLUR.key,
81
+ DefaultAssetSource.TYPEFACE.key,
82
+ DemoAssetSource.IMAGE.key,
83
+ DemoAssetSource.TEXT_COMPONENTS.key,
84
+ ).forEach { assetSource ->
85
+ launch {
86
+ val baseUri = editorContext.baseUri
87
+ editorContext.engine.populateAssetSource(
88
+ id = assetSource,
89
+ jsonUri = "$baseUri/$assetSource/content.json".toUri(),
90
+ replaceBaseUri = baseUri,
91
+ )
92
+ }
93
+ }
94
+ }
95
+
96
+ // Load local asset sources
97
+ editorContext.engine.asset.addLocalSource(
98
+ sourceId = DemoAssetSource.IMAGE_UPLOAD.key,
99
+ supportedMimeTypes = listOf(
100
+ "image/jpeg",
101
+ "image/png",
102
+ "image/heic",
103
+ "image/heif",
104
+ "image/svg+xml",
105
+ "image/gif",
106
+ "image/bmp",
107
+ ),
108
+ )
109
+
110
+ // Register gallery asset sources
111
+ listOf(
112
+ AssetSourceType.GalleryAllVisuals,
113
+ AssetSourceType.GalleryImage,
114
+ AssetSourceType.GalleryVideo,
115
+ ).forEach { type ->
116
+ editorContext.engine.asset.addSource(
117
+ source = SystemGalleryAssetSource(
118
+ context = editorContext.engine.applicationContext,
119
+ type = type,
120
+ ),
121
+ )
122
+ }
123
+ SystemGalleryPermission.setMode(systemGalleryConfiguration)
124
+
125
+ // Register text asset source
126
+ TypefaceProvider().provideTypeface(
127
+ engine = editorContext.engine,
128
+ name = "Roboto",
129
+ )?.let {
130
+ val textAssetSource = TextAssetSource(engine = editorContext.engine, typeface = it)
131
+ editorContext.engine.asset.addSource(textAssetSource)
132
+ }
133
+ }
134
+ // highlight-starter-kit-on-load-asset-sources
135
+
136
+ /**
137
+ * The callback that is invoked right after [onCreateScene], after the scene is created.
138
+ */
139
+ fun PostcardConfigurationBuilder.onPostCreateScene() {
140
+ // Do nothing
141
+ }
142
+
143
+ /**
144
+ * The callback that is invoked as the last step of [onCreate].
145
+ * It always runs, no matter success or failure on previous steps.
146
+ */
147
+ fun PostcardConfigurationBuilder.onCreateFinally() {
148
+ showLoading = false
149
+ }
@@ -0,0 +1,82 @@
1
+ package ly.img.editor.configuration.postcard.callback
2
+
3
+ import kotlinx.coroutines.CancellationException
4
+ import ly.img.editor.configuration.postcard.PostcardConfigurationBuilder
5
+ import ly.img.engine.MimeType
6
+ import java.nio.ByteBuffer
7
+
8
+ /**
9
+ * The callback that is invoked when the export button is clicked.
10
+ */
11
+ suspend fun PostcardConfigurationBuilder.onExport(
12
+ preExport: suspend PostcardConfigurationBuilder.() -> Unit = {
13
+ onPreExport()
14
+ },
15
+ exportByteBuffer: suspend PostcardConfigurationBuilder.() -> ByteBuffer = {
16
+ onExportByteBuffer()
17
+ },
18
+ postExport: suspend PostcardConfigurationBuilder.(ByteBuffer) -> Unit = {
19
+ onPostExport(it)
20
+ },
21
+ error: suspend PostcardConfigurationBuilder.(Exception) -> Unit = {
22
+ onExportError(it)
23
+ },
24
+ finally: suspend PostcardConfigurationBuilder.() -> Unit = {
25
+ onExportFinally()
26
+ },
27
+ ) {
28
+ try {
29
+ preExport()
30
+ val result = exportByteBuffer()
31
+ postExport(result)
32
+ } catch (exception: Exception) {
33
+ error(exception)
34
+ } finally {
35
+ finally()
36
+ }
37
+ }
38
+
39
+ /**
40
+ * The callback that is invoked before the export is started.
41
+ */
42
+ fun PostcardConfigurationBuilder.onPreExport() {
43
+ showLoading = true
44
+ }
45
+
46
+ /**
47
+ * The callback that exports the content of the editor into [ByteBuffer].
48
+ */
49
+ // highlight-starter-kit-on-export-byte-buffer
50
+ suspend fun PostcardConfigurationBuilder.onExportByteBuffer(): ByteBuffer = export(
51
+ block = requireNotNull(editorContext.engine.scene.get()),
52
+ mimeType = MimeType.PDF,
53
+ )
54
+ // highlight-starter-kit-on-export-byte-buffer
55
+
56
+ /**
57
+ * The callback that is invoked after [onExportByteBuffer] and handles its output.
58
+ */
59
+ // highlight-starter-kit-on-post-export
60
+ suspend fun PostcardConfigurationBuilder.onPostExport(byteBuffer: ByteBuffer) {
61
+ val file = writeToFile(byteBuffer = byteBuffer, mimeType = MimeType.PDF)
62
+ shareFile(file = file, mimeType = MimeType.PDF)
63
+ }
64
+ // highlight-starter-kit-on-post-export
65
+
66
+ /**
67
+ * The callback that is invoked in case any of the export functions throw an exception.
68
+ */
69
+ fun PostcardConfigurationBuilder.onExportError(error: Exception) {
70
+ if (error is CancellationException) {
71
+ throw error
72
+ }
73
+ this.error = error
74
+ }
75
+
76
+ /**
77
+ * The callback that is invoked as the last step of [onExportByteBuffer].
78
+ * It always runs, no matter success or failure on previous steps.
79
+ */
80
+ fun PostcardConfigurationBuilder.onExportFinally() {
81
+ showLoading = false
82
+ }
@@ -0,0 +1,58 @@
1
+ package ly.img.editor.configuration.postcard.callback
2
+
3
+ import android.content.res.Configuration
4
+ import kotlinx.coroutines.coroutineScope
5
+ import kotlinx.coroutines.flow.distinctUntilChangedBy
6
+ import kotlinx.coroutines.flow.filter
7
+ import kotlinx.coroutines.launch
8
+ import ly.img.editor.configuration.postcard.PostcardConfigurationBuilder
9
+ import ly.img.editor.core.state.EditorViewMode
10
+ import ly.img.engine.DesignBlockType
11
+
12
+ /**
13
+ * The callback that is invoked when the editor is loaded and ready to be used.
14
+ */
15
+ suspend fun PostcardConfigurationBuilder.onLoaded() {
16
+ coroutineScope {
17
+ launch { observeEditorViewMode() }
18
+ launch { observeEditorEditMode() }
19
+ }
20
+ }
21
+
22
+ /**
23
+ * Function that displays both pages vertically stacked and zooms out so that both of the pages are visible
24
+ * when the [EditorViewMode] is preview.
25
+ */
26
+ suspend fun PostcardConfigurationBuilder.observeEditorViewMode(stackSpacing: Float = 16F) {
27
+ editorContext.state
28
+ .distinctUntilChangedBy { it.viewMode to it.insets }
29
+ .filter { it.viewMode is EditorViewMode.Preview }
30
+ .collect { state ->
31
+ val engine = editorContext.engine
32
+ // Display pages vertically stacked
33
+ val axis = if (editorContext.activity.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
34
+ "Horizontal"
35
+ } else {
36
+ "Vertical"
37
+ }
38
+ engine.block
39
+ .findByType(DesignBlockType.Stack)
40
+ .firstOrNull()
41
+ ?.let {
42
+ engine.block.setEnum(block = it, property = "stack/axis", value = axis)
43
+ engine.block.setFloat(block = it, property = "stack/spacing", value = stackSpacing)
44
+ }
45
+ // Display both pages in preview mode
46
+ engine.scene.getPages().forEach {
47
+ engine.block.setVisible(block = it, visible = true)
48
+ }
49
+ engine.scene.immediateZoomToBlock(
50
+ block = requireNotNull(engine.scene.get()),
51
+ paddingLeft = state.insets.left.value,
52
+ paddingTop = state.insets.top.value,
53
+ paddingRight = state.insets.right.value,
54
+ paddingBottom = 0F,
55
+ forceUpdate = true,
56
+ )
57
+ }
58
+ }
@@ -0,0 +1,45 @@
1
+ @file:Suppress("UnusedReceiverParameter")
2
+
3
+ package ly.img.editor.configuration.postcard.component
4
+
5
+ import androidx.compose.runtime.Composable
6
+ import ly.img.editor.configuration.postcard.PostcardConfigurationBuilder
7
+ import ly.img.editor.core.component.CanvasMenu
8
+ import ly.img.editor.core.component.EditorComponentId
9
+ import ly.img.editor.core.component.remember
10
+ import ly.img.editor.core.component.rememberBringForward
11
+ import ly.img.editor.core.component.rememberDelete
12
+ import ly.img.editor.core.component.rememberDuplicate
13
+ import ly.img.editor.core.component.rememberSelectGroup
14
+ import ly.img.editor.core.component.rememberSendBackward
15
+
16
+ /**
17
+ * The configuration of the component that is displayed as horizontal list of items next to
18
+ * the selected design block.
19
+ */
20
+ @Composable
21
+ fun PostcardConfigurationBuilder.rememberCanvasMenu() = CanvasMenu.remember {
22
+ listBuilder = {
23
+ CanvasMenu.ListBuilder.remember {
24
+ add { CanvasMenu.Button.rememberSelectGroup() }
25
+ if (editorContext.isSelectionInGroup) {
26
+ add {
27
+ CanvasMenu.Divider.remember {
28
+ id = { EditorComponentId("ly.img.component.canvasMenu.divider1") }
29
+ }
30
+ }
31
+ }
32
+ add { CanvasMenu.Button.rememberBringForward() }
33
+ add { CanvasMenu.Button.rememberSendBackward() }
34
+ if (editorContext.canSelectionMove) {
35
+ add {
36
+ CanvasMenu.Divider.remember {
37
+ id = { EditorComponentId("ly.img.component.canvasMenu.divider2") }
38
+ }
39
+ }
40
+ }
41
+ add { CanvasMenu.Button.rememberDuplicate() }
42
+ add { CanvasMenu.Button.rememberDelete() }
43
+ }
44
+ }
45
+ }
@@ -0,0 +1,243 @@
1
+ @file:OptIn(UnstableEditorApi::class)
2
+ @file:Suppress("UnusedReceiverParameter", "UnusedFlow")
3
+
4
+ package ly.img.editor.configuration.postcard.component
5
+
6
+ import androidx.compose.foundation.layout.Arrangement
7
+ import androidx.compose.runtime.Composable
8
+ import androidx.compose.runtime.Stable
9
+ import androidx.compose.runtime.collectAsState
10
+ import androidx.compose.runtime.getValue
11
+ import androidx.compose.runtime.remember
12
+ import androidx.compose.ui.graphics.Color
13
+ import androidx.compose.ui.res.stringResource
14
+ import kotlinx.coroutines.flow.emptyFlow
15
+ import kotlinx.coroutines.flow.map
16
+ import ly.img.editor.configuration.postcard.PostcardConfigurationBuilder
17
+ import ly.img.editor.configuration.postcard.ext.getCurrentPageIndex
18
+ import ly.img.editor.configuration.postcard.ext.getFill
19
+ import ly.img.editor.configuration.postcard.ext.getStrokeColor
20
+ import ly.img.editor.core.EditorContext
21
+ import ly.img.editor.core.EditorScope
22
+ import ly.img.editor.core.R
23
+ import ly.img.editor.core.ScopedProperty
24
+ import ly.img.editor.core.UnstableEditorApi
25
+ import ly.img.editor.core.component.AbstractButtonBuilder
26
+ import ly.img.editor.core.component.Button
27
+ import ly.img.editor.core.component.DefaultDecoration
28
+ import ly.img.editor.core.component.Dock
29
+ import ly.img.editor.core.component.EditorComponentId
30
+ import ly.img.editor.core.component.EditorTrigger
31
+ import ly.img.editor.core.component.data.EditorIcon
32
+ import ly.img.editor.core.component.data.GradientFill
33
+ import ly.img.editor.core.component.data.SolidFill
34
+ import ly.img.editor.core.component.remember
35
+ import ly.img.editor.core.component.rememberAssetLibrary
36
+ import ly.img.editor.core.event.EditorEvent
37
+ import ly.img.editor.core.iconpack.IconPack
38
+ import ly.img.editor.core.iconpack.SizeLCircled
39
+ import ly.img.editor.core.iconpack.SizeMCircled
40
+ import ly.img.editor.core.iconpack.SizeSCircled
41
+ import ly.img.editor.core.iconpack.Typeface
42
+ import ly.img.editor.core.sheet.SheetType
43
+ import ly.img.editor.core.state.EditorViewMode
44
+ import ly.img.editor.core.ui.EditorIcon
45
+ import ly.img.engine.DesignBlock
46
+ import ly.img.engine.DesignBlockType
47
+
48
+ /**
49
+ * The configuration of the component that is displayed as horizontal list of items at the bottom of the editor.
50
+ */
51
+ @Composable
52
+ fun PostcardConfigurationBuilder.rememberDock() = Dock.remember {
53
+ scope = {
54
+ val activeSceneTrigger by EditorTrigger.remember {
55
+ editorContext.engine.scene.onActiveChanged()
56
+ }
57
+ val historyTrigger by EditorTrigger.remember(activeSceneTrigger) {
58
+ editorContext.engine.editor.onHistoryUpdated()
59
+ }
60
+ val pageIndex by remember(this, activeSceneTrigger) {
61
+ if (editorContext.engine.scene.get() == null) return@remember emptyFlow()
62
+ val stack = editorContext.engine.block.findByType(DesignBlockType.Stack).first()
63
+ editorContext.engine.event.subscribe(listOf(stack))
64
+ .map { editorContext.engine.getCurrentPageIndex() }
65
+ }.collectAsState(initial = 0)
66
+ // Update Dock whenever the active scene, editor history or current page index changes.
67
+ remember(this, pageIndex, historyTrigger) {
68
+ Dock.Scope(parentScope = this)
69
+ }
70
+ }
71
+ visible = {
72
+ val state by editorContext.state.collectAsState()
73
+ // Hide Dock in preview mode
74
+ state.viewMode !is EditorViewMode.Preview
75
+ }
76
+ decoration = {
77
+ // Show transparent background
78
+ Dock.DefaultDecoration(
79
+ background = Color.Transparent,
80
+ ) { it() }
81
+ }
82
+ horizontalArrangement = {
83
+ remember(this) {
84
+ val isFirstPage = editorContext.engine.scene.get() != null &&
85
+ editorContext.engine.getCurrentPageIndex() == 0
86
+ // Show Dock start aligned on the first page and centered on the second page
87
+ if (isFirstPage) Arrangement.Start else Arrangement.Center
88
+ }
89
+ }
90
+ listBuilder = {
91
+ Dock.ListBuilder.remember {
92
+ add {
93
+ Dock.Button.rememberAssetLibrary {
94
+ visible = {
95
+ remember(this) {
96
+ editorContext.engine.getCurrentPageIndex() == 0
97
+ }
98
+ }
99
+ }
100
+ }
101
+ // Show divider only on the first page.
102
+ if (editorContext.engine.getCurrentPageIndex() == 0) {
103
+ add { Dock.Divider.remember() }
104
+ }
105
+ add { Dock.Button.rememberGreetingFont() }
106
+ add { Dock.Button.rememberGreetingSize() }
107
+ add { Dock.Button.rememberColors() }
108
+ }
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Greeting font button at the dock.
114
+ */
115
+ @Composable
116
+ fun Dock.Button.rememberGreetingFont() = Dock.Button.remember {
117
+ id = { EditorComponentId("ly.img.component.dock.button.greetingFont") }
118
+ visible = {
119
+ remember(this) {
120
+ editorContext.engine.scene.run { getPages().indexOf(getCurrentPage()) == 1 }
121
+ }
122
+ }
123
+ vectorIcon = { IconPack.Typeface }
124
+ textString = { stringResource(R.string.ly_img_editor_dock_button_font) }
125
+ onClick = {
126
+ val designBlock = editorContext.engine.block.findByName("Greeting").first()
127
+ editorContext.eventHandler.send(
128
+ EditorEvent.Sheet.Open(SheetType.Font(designBlock = designBlock)),
129
+ )
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Greeting size button of the dock.
135
+ */
136
+ @Composable
137
+ fun Dock.Button.rememberGreetingSize() = Dock.Button.remember {
138
+ id = { EditorComponentId("ly.img.component.dock.button.greetingSize") }
139
+ visible = {
140
+ remember(this) {
141
+ editorContext.engine.scene.run { getPages().indexOf(getCurrentPage()) == 1 }
142
+ }
143
+ }
144
+ vectorIcon = {
145
+ remember(this) {
146
+ val block = editorContext.engine.block.findByName("Greeting").firstOrNull()
147
+ requireNotNull(block) { "Page must have design block with name \"Greeting\"." }
148
+ val size = editorContext.engine.block.getFloat(block, "text/fontSize")
149
+ when {
150
+ size <= 14F -> IconPack.SizeSCircled
151
+ size > 18F -> IconPack.SizeLCircled
152
+ else -> IconPack.SizeMCircled
153
+ }
154
+ }
155
+ }
156
+ textString = { stringResource(R.string.ly_img_editor_dock_button_size) }
157
+ onClick = {
158
+ val designBlock = editorContext.engine.block.findByName("Greeting").first()
159
+ editorContext.eventHandler.send(
160
+ EditorEvent.Sheet.Open(SheetType.FontSize(designBlock = designBlock)),
161
+ )
162
+ }
163
+ }
164
+
165
+ /**
166
+ * Colors button of the dock.
167
+ */
168
+ @Stable
169
+ open class ColorsButtonScope(
170
+ parentScope: EditorScope,
171
+ private val colors: Map<DesignBlock, Color>,
172
+ ) : Dock.ItemScope(parentScope) {
173
+ val EditorContext.colors: Map<DesignBlock, Color>
174
+ get() = this@ColorsButtonScope.colors
175
+ }
176
+
177
+ /**
178
+ * Button builder of the [rememberColors] button.
179
+ */
180
+ @Stable
181
+ class ColorsButtonBuilder : AbstractButtonBuilder<ColorsButtonScope>() {
182
+ override var scope: ScopedProperty<EditorScope, ColorsButtonScope> = {
183
+ remember(this) {
184
+ val engine = editorContext.engine
185
+ val colorMapping = sortedMapOf<String, Pair<DesignBlock, Color>>()
186
+ engine.scene.getCurrentPage()?.let { currentPage ->
187
+ val target = engine.block.findByName("Greeting")
188
+ .firstOrNull()
189
+ ?.takeIf { engine.block.getParent(it) == currentPage }
190
+ ?: currentPage
191
+ editorContext.collectColorsRecursively(target, colorMapping)
192
+ }
193
+ val colors = colorMapping.values.associate { (block, color) -> block to color }
194
+ ColorsButtonScope(parentScope = this, colors = colors)
195
+ }
196
+ }
197
+
198
+ private fun EditorContext.collectColorsRecursively(
199
+ designBlock: DesignBlock,
200
+ colorMapping: MutableMap<String, Pair<DesignBlock, Color>>,
201
+ ) {
202
+ val name = engine.block.getName(designBlock)
203
+ if (name.isNotEmpty()) {
204
+ val color = if (engine.block.supportsFill(designBlock) && engine.block.isFillEnabled(designBlock)) {
205
+ when (val fillInfo = engine.getFill(designBlock)) {
206
+ is SolidFill, is GradientFill -> fillInfo.mainColor
207
+ else -> null
208
+ }
209
+ } else if (engine.block.supportsStroke(designBlock) && engine.block.isStrokeEnabled(designBlock)) {
210
+ engine.getStrokeColor(designBlock)
211
+ } else {
212
+ null
213
+ }
214
+ color?.let { colorMapping[name] = designBlock to color }
215
+ }
216
+ engine.block.getChildren(designBlock).forEach {
217
+ collectColorsRecursively(it, colorMapping)
218
+ }
219
+ }
220
+ }
221
+
222
+ /**
223
+ * Colors button of the dock.
224
+ */
225
+ @Composable
226
+ fun Dock.Button.rememberColors() = Button.remember(::ColorsButtonBuilder) {
227
+ id = { EditorComponentId("ly.img.component.dock.button.colors") }
228
+ icon = {
229
+ val icon = remember(this) {
230
+ EditorIcon.Colors(
231
+ colors = editorContext.colors.values.toList(),
232
+ )
233
+ }
234
+ EditorIcon(icon = icon)
235
+ }
236
+ textString = { stringResource(R.string.ly_img_editor_dock_button_colors) }
237
+ onClick = {
238
+ editorContext.colors
239
+ .let { SheetType.Colors(designBlocks = it.keys.toList()) }
240
+ .let(EditorEvent.Sheet::Open)
241
+ .let(editorContext.eventHandler::send)
242
+ }
243
+ }
@@ -0,0 +1,52 @@
1
+ @file:Suppress("UnusedReceiverParameter")
2
+
3
+ package ly.img.editor.configuration.postcard.component
4
+
5
+ import androidx.compose.runtime.Composable
6
+ import ly.img.editor.configuration.postcard.PostcardConfigurationBuilder
7
+ import ly.img.editor.core.component.InspectorBar
8
+ import ly.img.editor.core.component.remember
9
+ import ly.img.editor.core.component.rememberAdjustments
10
+ import ly.img.editor.core.component.rememberBlur
11
+ import ly.img.editor.core.component.rememberCrop
12
+ import ly.img.editor.core.component.rememberDelete
13
+ import ly.img.editor.core.component.rememberDuplicate
14
+ import ly.img.editor.core.component.rememberEditText
15
+ import ly.img.editor.core.component.rememberEffect
16
+ import ly.img.editor.core.component.rememberEnterGroup
17
+ import ly.img.editor.core.component.rememberFillStroke
18
+ import ly.img.editor.core.component.rememberFilter
19
+ import ly.img.editor.core.component.rememberFormatText
20
+ import ly.img.editor.core.component.rememberLayer
21
+ import ly.img.editor.core.component.rememberReplace
22
+ import ly.img.editor.core.component.rememberSelectGroup
23
+ import ly.img.editor.core.component.rememberShape
24
+ import ly.img.editor.core.component.rememberTextBackground
25
+
26
+ /**
27
+ * The configuration of the component that is displayed as horizontal list of items at the
28
+ * bottom of the editor when a design block is selected.
29
+ */
30
+ @Composable
31
+ fun PostcardConfigurationBuilder.rememberInspectorBar() = InspectorBar.remember {
32
+ listBuilder = {
33
+ InspectorBar.ListBuilder.remember {
34
+ add { InspectorBar.Button.rememberReplace() } // Image, Sticker, Audio
35
+ add { InspectorBar.Button.rememberEditText() } // Text
36
+ add { InspectorBar.Button.rememberFormatText() } // Text
37
+ add { InspectorBar.Button.rememberFillStroke() } // Page, Image, Shape, Text
38
+ add { InspectorBar.Button.rememberTextBackground() } // Text
39
+ add { InspectorBar.Button.rememberCrop() } // Image
40
+ add { InspectorBar.Button.rememberAdjustments() } // Image
41
+ add { InspectorBar.Button.rememberFilter() } // Image
42
+ add { InspectorBar.Button.rememberEffect() } // Image
43
+ add { InspectorBar.Button.rememberBlur() } // Image
44
+ add { InspectorBar.Button.rememberShape() } // Image, Shape
45
+ add { InspectorBar.Button.rememberSelectGroup() } // Image, Sticker, Shape, Text
46
+ add { InspectorBar.Button.rememberEnterGroup() } // Group
47
+ add { InspectorBar.Button.rememberLayer() } // Image, Sticker, Shape, Text
48
+ add { InspectorBar.Button.rememberDuplicate() } // Image, Sticker, Shape, Text
49
+ add { InspectorBar.Button.rememberDelete() } // Image, Sticker, Shape, Text
50
+ }
51
+ }
52
+ }