Skip to content

Commit 291cfa8

Browse files
committed
[Gradle] Add autoMirror configuration support for ImageVectors at multiple levels
1 parent 36d72fd commit 291cfa8

File tree

8 files changed

+602
-1
lines changed

8 files changed

+602
-1
lines changed

README.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,13 @@ valkyrie {
416416
// Optional: Generate during IDE sync for better developer experience (default: false)
417417
generateAtSync = false
418418

419+
// Optional: Force all generated ImageVectors to have a specific autoMirror value (default: not specified)
420+
// When set to true, all icons will have autoMirror = true
421+
// When set to false, all icons will have autoMirror = false
422+
// When not specified, the autoMirror value from the original icon file will be preserved
423+
// This can be overridden at the icon pack or nested pack level
424+
autoMirror = false
425+
419426
// Optional: Code style configuration for generated code
420427
codeStyle {
421428
// Add explicit `public` modifier to generated declarations (default: false)
@@ -454,13 +461,22 @@ valkyrie {
454461
// Optional: Generate flat package structure without subfolders (default: false)
455462
useFlatPackage = false
456463

464+
// Optional: Force all ImageVectors in this icon pack to have a specific autoMirror value (default: not specified)
465+
// When set, overrides the root level autoMirror setting
466+
// This can be overridden at the nested pack level
467+
autoMirror = true
468+
457469
// Optional: Nested icon packs configuration
458470
nested {
459471
// Required: Name of the nested icon pack object
460472
name = "Outlined"
461473

462474
// Required: The source folder path containing icons for this nested pack, relative to the `resourceDirectoryName`.
463475
sourceFolder = "outlined"
476+
477+
// Optional: Force all ImageVectors in this nested pack to have a specific autoMirror value (default: not specified)
478+
// When set, overrides the icon pack level and root level autoMirror settings
479+
autoMirror = false
464480
}
465481
// You can add more nested packs if necessary
466482
}
@@ -708,6 +724,50 @@ tasks.register("updateValkyrieIcons") {
708724
}
709725
```
710726

727+
#### AutoMirror configuration
728+
729+
The `autoMirror` parameter controls whether icons should automatically flip horizontally when used in right-to-left (
730+
RTL) layouts. This is particularly useful for directional icons like arrows, chevrons, or navigation elements.
731+
732+
**Configuration hierarchy:**
733+
734+
The plugin supports a three-level hierarchy for `autoMirror` configuration:
735+
736+
1. **Root level** - applies to all icons across the project
737+
2. **Icon pack level** - overrides root level for all icons in the pack
738+
3. **Nested pack level** - overrides both icon pack and root level for icons in the nested pack
739+
740+
For example, to force enable RTL support for icons in the `Navigation` nested pack:
741+
742+
```kotlin
743+
valkyrie {
744+
packageName = "com.example.app.icons"
745+
746+
iconPack {
747+
name = "ValkyrieIcons"
748+
targetSourceSet = "commonMain"
749+
750+
nested {
751+
name = "Navigation"
752+
sourceFolder = "navigation"
753+
754+
autoMirror = true
755+
}
756+
757+
nested {
758+
name = "Logos"
759+
sourceFolder = "logos"
760+
}
761+
}
762+
}
763+
```
764+
765+
In this example:
766+
767+
- Icons in the `Navigation` nested pack will have `autoMirror = true`
768+
- Icons in the `Logos` nested pack (and any other nested pack without an explicit `autoMirror` setting) will preserve
769+
the original `autoMirror` value from the source icon file
770+
711771
## Other
712772

713773
### Export formats

tools/gradle-plugin/CHANGELOG.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,50 @@
66

77
- Automatically handle full qualified imports for icons that conflict with reserved Compose qualified names (`Brush`,
88
`Color`, `Offset`)
9+
- Add `autoMirror` configuration support at root, icon pack, and nested pack levels to control RTL (right-to-left)
10+
layout behavior for generated ImageVectors
11+
12+
The `autoMirror` parameter controls whether icons should automatically flip horizontally when used in RTL layouts.
13+
This is particularly useful for directional icons like arrows, chevrons, or navigation elements.
14+
15+
Configuration can be set at three levels with override hierarchy:
16+
1. **Root level** - applies to all icons across the project
17+
2. **Icon pack level** - overrides root level for all icons in the pack
18+
3. **Nested pack level** - overrides both icon pack and root level for icons in the nested pack
19+
20+
Example configuration:
21+
22+
```kotlin
23+
valkyrie {
24+
packageName = "com.example.app.icons"
25+
26+
// Force all icons to support RTL by default
27+
autoMirror = true
28+
29+
iconPack {
30+
name = "ValkyrieIcons"
31+
targetSourceSet = "commonMain"
32+
33+
// Override: icons in this pack won't auto-mirror
34+
autoMirror = false
35+
36+
nested {
37+
name = "Navigation"
38+
sourceFolder = "navigation"
39+
// Override: navigation icons should auto-mirror for RTL
40+
autoMirror = true
41+
}
42+
43+
nested {
44+
name = "Logos"
45+
sourceFolder = "logos"
46+
// Logos inherit autoMirror = false from icon pack level
47+
}
48+
}
49+
}
50+
```
51+
52+
When `autoMirror` is not specified at any level, the original value from the source icon file will be preserved.
953

1054
### Changed
1155

tools/gradle-plugin/api/gradle-plugin.api

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ public abstract class io/github/composegears/valkyrie/gradle/CodeStyleConfigExte
66

77
public abstract class io/github/composegears/valkyrie/gradle/IconPackExtension {
88
public fun <init> (Lorg/gradle/api/model/ObjectFactory;)V
9+
public final fun getAutoMirror ()Lorg/gradle/api/provider/Property;
910
public final fun getName ()Lorg/gradle/api/provider/Property;
1011
public final fun getTargetSourceSet ()Lorg/gradle/api/provider/Property;
1112
public final fun getUseFlatPackage ()Lorg/gradle/api/provider/Property;
@@ -23,13 +24,15 @@ public abstract class io/github/composegears/valkyrie/gradle/ImageVectorConfigEx
2324

2425
public abstract class io/github/composegears/valkyrie/gradle/NestedPack {
2526
public fun <init> (Lorg/gradle/api/model/ObjectFactory;)V
27+
public final fun getAutoMirror ()Lorg/gradle/api/provider/Property;
2628
public final fun getName ()Lorg/gradle/api/provider/Property;
2729
public final fun getSourceFolder ()Lorg/gradle/api/provider/Property;
2830
}
2931

3032
public abstract class io/github/composegears/valkyrie/gradle/ValkyrieExtension {
3133
public fun <init> (Lorg/gradle/api/model/ObjectFactory;)V
3234
public final fun codeStyle (Lkotlin/jvm/functions/Function1;)V
35+
public final fun getAutoMirror ()Lorg/gradle/api/provider/Property;
3336
public final fun getGenerateAtSync ()Lorg/gradle/api/provider/Property;
3437
public final fun getOutputDirectory ()Lorg/gradle/api/file/DirectoryProperty;
3538
public final fun getPackageName ()Lorg/gradle/api/provider/Property;

tools/gradle-plugin/src/main/kotlin/io/github/composegears/valkyrie/gradle/IconPackExtension.kt

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import org.gradle.api.provider.ListProperty
99
import org.gradle.api.provider.Property
1010
import org.gradle.api.tasks.Input
1111
import org.gradle.api.tasks.Nested
12+
import org.gradle.api.tasks.Optional
1213
import org.gradle.declarative.dsl.model.annotations.Configuring
1314

1415
abstract class IconPackExtension @Inject constructor(
@@ -40,6 +41,21 @@ abstract class IconPackExtension @Inject constructor(
4041
.property<Boolean>()
4142
.convention(false)
4243

44+
/**
45+
* Force all ImageVectors in this icon pack to have a specific autoMirror value.
46+
*
47+
* When set to `true`, all icons in this pack will have `autoMirror = true`.
48+
* When set to `false`, all icons in this pack will have `autoMirror = false`.
49+
* When not specified, the autoMirror value from the root extension or the original icon file will be used.
50+
*
51+
* This can be overridden at the nested pack level.
52+
*
53+
* Default: not specified
54+
*/
55+
@get:Input
56+
@get:Optional
57+
val autoMirror: Property<Boolean> = objects.property<Boolean>()
58+
4359
@get:Nested
4460
internal val nestedPacks: ListProperty<NestedPack> = objects
4561
.listProperty<NestedPack>()
@@ -92,4 +108,17 @@ abstract class NestedPack @Inject constructor(objects: ObjectFactory) {
92108
*/
93109
@get:Input
94110
val sourceFolder: Property<String> = objects.property<String>()
111+
112+
/**
113+
* Force all ImageVectors in this nested pack to have a specific autoMirror value.
114+
*
115+
* When set to `true`, all icons in this nested pack will have `autoMirror = true`.
116+
* When set to `false`, all icons in this nested pack will have `autoMirror = false`.
117+
* When not specified, the autoMirror value from the icon pack or root extension will be used.
118+
*
119+
* Default: not specified
120+
*/
121+
@get:Input
122+
@get:Optional
123+
val autoMirror: Property<Boolean> = objects.property<Boolean>()
95124
}

tools/gradle-plugin/src/main/kotlin/io/github/composegears/valkyrie/gradle/ValkyrieExtension.kt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,20 @@ abstract class ValkyrieExtension @Inject constructor(private val objects: Object
5757
.property<String>()
5858
.convention(DEFAULT_RESOURCE_DIRECTORY)
5959

60+
/**
61+
* Force all generated ImageVectors to have a specific autoMirror value.
62+
*
63+
* When set to `true`, all icons will have `autoMirror = true`.
64+
* When set to `false`, all icons will have `autoMirror = false`.
65+
* When not specified, the autoMirror value from the original icon file will be preserved.
66+
*
67+
* This can be overridden at the icon pack or nested pack level.
68+
*
69+
* Default: not specified
70+
*/
71+
@get:Optional
72+
val autoMirror: Property<Boolean> = objects.property<Boolean>()
73+
6074
/**
6175
* Code style configuration for generated code.
6276
*/

tools/gradle-plugin/src/main/kotlin/io/github/composegears/valkyrie/gradle/internal/TaskRegistry.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ internal fun registerTask(
4242
task.useExplicitMode.convention(extension.codeStyle.useExplicitMode)
4343
task.addTrailingComma.convention(extension.imageVector.addTrailingComma)
4444
task.indentSize.convention(extension.codeStyle.indentSize)
45+
task.autoMirror.convention(extension.autoMirror)
4546

4647
task.sourceSet.convention(sourceSet.name)
4748
task.iconPack.convention(extension.iconPack)

tools/gradle-plugin/src/main/kotlin/io/github/composegears/valkyrie/gradle/internal/task/GenerateImageVectorsTask.kt

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ internal abstract class GenerateImageVectorsTask : DefaultTask() {
6161
@get:Input
6262
abstract val indentSize: Property<Int>
6363

64+
@get:Optional
65+
@get:Input
66+
abstract val autoMirror: Property<Boolean>
67+
6468
@get:OutputDirectory
6569
abstract val outputDirectory: DirectoryProperty
6670

@@ -310,8 +314,17 @@ internal abstract class GenerateImageVectorsTask : DefaultTask() {
310314
nestedPackName: String?,
311315
) {
312316
val parseOutput = SvgXmlParser.toIrImageVector(ParserType.Jvm, Path(file.absolutePath))
317+
318+
// Apply autoMirror override if specified (nested pack > icon pack > root)
319+
val effectiveAutoMirror = resolveAutoMirror(nestedPackName)
320+
val irImageVector = if (effectiveAutoMirror != null) {
321+
parseOutput.irImageVector.copy(autoMirror = effectiveAutoMirror)
322+
} else {
323+
parseOutput.irImageVector
324+
}
325+
313326
val vectorSpecOutput = ImageVectorGenerator.convert(
314-
vector = parseOutput.irImageVector,
327+
vector = irImageVector,
315328
iconName = parseOutput.iconName,
316329
config = config,
317330
)
@@ -359,4 +372,24 @@ internal abstract class GenerateImageVectorsTask : DefaultTask() {
359372
private fun logFileParseError(file: File, error: Throwable) {
360373
logger.warn("Skipping file ${file.name} due to processing error, details: ${error.message}")
361374
}
375+
376+
private fun resolveAutoMirror(nestedPackName: String?): Boolean? {
377+
// Priority: nested pack > icon pack > root extension
378+
if (iconPack.isPresent && nestedPackName != null) {
379+
val nestedPack = iconPack.get().nestedPacks.get().find { it.name.get() == nestedPackName }
380+
if (nestedPack?.autoMirror?.isPresent == true) {
381+
return nestedPack.autoMirror.get()
382+
}
383+
}
384+
385+
if (iconPack.isPresent && iconPack.get().autoMirror.isPresent) {
386+
return iconPack.get().autoMirror.get()
387+
}
388+
389+
if (autoMirror.isPresent) {
390+
return autoMirror.get()
391+
}
392+
393+
return null
394+
}
362395
}

0 commit comments

Comments
 (0)