diff --git a/README.md b/README.md
index c7f7c1dd..6eeba21a 100644
--- a/README.md
+++ b/README.md
@@ -1463,16 +1463,8 @@ Support levels:
.backgroundStyle |
- | 🟡 |
-
-
- .badge
-
- - Supported on
List items
- - Not yet supported on
TabView
-
-
- |
+ ✅ |
+ .badge |
| 🟢 |
diff --git a/Sources/SkipUI/SkipUI/Containers/List.swift b/Sources/SkipUI/SkipUI/Containers/List.swift
index 9d37af99..eb28086d 100644
--- a/Sources/SkipUI/SkipUI/Containers/List.swift
+++ b/Sources/SkipUI/SkipUI/Containers/List.swift
@@ -903,30 +903,6 @@ final class ListItemModifier: RenderModifier {
return ListItemModifier(background: background, separator: separator)
}
}
-
-final class BadgeModifier: RenderModifier {
- let badge: Text?
- let prominence: BadgeProminence?
-
- init(badge: Text? = nil, prominence: BadgeProminence? = nil) {
- self.badge = badge
- self.prominence = prominence
- super.init()
- }
-
- static func combined(for renderable: Renderable) -> BadgeModifier {
- var badge: Text? = nil
- var prominence: BadgeProminence? = nil
- renderable.forEachModifier {
- if let badgeModifier = $0 as? BadgeModifier {
- badge = badge ?? badgeModifier.badge
- prominence = prominence ?? badgeModifier.prominence
- }
- return nil
- }
- return BadgeModifier(badge: badge, prominence: prominence)
- }
-}
#endif
#if false
diff --git a/Sources/SkipUI/SkipUI/Containers/TabView.swift b/Sources/SkipUI/SkipUI/Containers/TabView.swift
index ab2f7f54..65b07a56 100644
--- a/Sources/SkipUI/SkipUI/Containers/TabView.swift
+++ b/Sources/SkipUI/SkipUI/Containers/TabView.swift
@@ -20,6 +20,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.layout.ime
+import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.safeDrawing
@@ -39,6 +40,8 @@ import androidx.compose.material3.NavigationBarItemDefaults
import androidx.compose.material3.NavigationRail
import androidx.compose.material3.NavigationRailItem
import androidx.compose.material3.NavigationRailItemDefaults
+import androidx.compose.material3.Badge
+import androidx.compose.material3.BadgedBox
import androidx.compose.material3.contentColorFor
import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScaffoldDefaults
@@ -228,10 +231,16 @@ public struct TabView : View, Renderable {
let tabRenderables = EvaluateContent(context: tabContext)
let tabs: kotlin.collections.List = tabRenderables.map {
let renderable = $0 as Renderable // Let transpiler understand type
- if let tab = renderable.strip() as? Tab {
+ let badgeModifier = BadgeModifier.combined(for: renderable)
+ if var tab = renderable.strip() as? Tab {
+ if let badge = badgeModifier.badge {
+ tab.badge = badge
+ }
return tab
} else if let tabItemModifier = renderable.forEachModifier(perform: { $0 as? TabItemModifier }) as? TabItemModifier {
- return Tab(content: { renderable.asView() }, label: { tabItemModifier.label })
+ var tab = Tab(content: { renderable.asView() }, label: { tabItemModifier.label })
+ tab.badge = badgeModifier.badge
+ return tab
} else {
return nil
}
@@ -743,6 +752,7 @@ public enum AdaptableTabBarPlacement : Hashable {
public protocol TabContent : View {
var isHidden: Bool { get set }
var isDisabled: Bool { get set }
+ var badge: Text? { get set }
}
// SKIP @bridge
@@ -850,6 +860,7 @@ public struct Tab : TabContent, Renderable {
public var isHidden = false
public var isDisabled = false
+ public var badge: Text? = nil
#if SKIP
@Composable override func Render(context: ComposeContext) {
@@ -888,8 +899,11 @@ public struct Tab : TabContent, Renderable {
renderable.Render(context: context)
}
}
- Box(modifier: outerModifier, contentAlignment: androidx.compose.ui.Alignment.Center) {
- Box(modifier: Modifier.graphicsLayer(scaleX: Float(1.5), scaleY: Float(1.5)), contentAlignment: androidx.compose.ui.Alignment.Center) {
+ let renderIcon: (@Composable () -> ()) = {
+ Box(
+ modifier: Modifier.graphicsLayer(scaleX: Float(1.5), scaleY: Float(1.5)),
+ contentAlignment: androidx.compose.ui.Alignment.Center
+ ) {
// Default to a lighter symbol weight so tab icons approximate SwiftUI sizing
if EnvironmentValues.shared._textEnvironment.fontWeight == nil {
EnvironmentValues.shared.setValues {
@@ -905,6 +919,22 @@ public struct Tab : TabContent, Renderable {
}
}
}
+
+ Box(modifier: outerModifier, contentAlignment: androidx.compose.ui.Alignment.Center) {
+ if let badge {
+ BadgedBox(
+ badge: {
+ Badge(modifier: Modifier.offset(x: 8.dp, y: -4.dp)) {
+ badge.foregroundStyle(Color.white).Render(context: context)
+ }
+ }
+ ) {
+ renderIcon()
+ }
+ } else {
+ renderIcon()
+ }
+ }
}
#else
public var body: some View {
@@ -960,6 +990,7 @@ public struct TabSection : TabContent, Renderable {
public var isHidden = false
public var isDisabled = false
+ public var badge: Text? = nil
#if SKIP
@Composable override func Evaluate(context: ComposeContext, options: Int) -> kotlin.collections.List {
@@ -971,6 +1002,9 @@ public struct TabSection : TabContent, Renderable {
if isDisabled {
(renderable as? TabContent)?.isDisabled = true
}
+ if let badge {
+ (renderable as? TabContent)?.badge = badge
+ }
}
return renderables
}
@@ -1078,29 +1112,34 @@ extension TabContent {
return self
}
- @available(*, unavailable)
public func badge(_ count: Int) -> some TabContent {
- return self
+ var tabContent = self
+ tabContent.badge = count > 0 ? Text(String(count)) : nil
+ return tabContent
}
- @available(*, unavailable)
public func badge(_ label: Text?) -> some TabContent {
- return self
+ var tabContent = self
+ tabContent.badge = label
+ return tabContent
}
- @available(*, unavailable)
public func badge(_ key: LocalizedStringKey) -> some TabContent {
- return self
+ var tabContent = self
+ tabContent.badge = Text(key)
+ return tabContent
}
- @available(*, unavailable)
public func badge(_ resource: LocalizedStringResource) -> some TabContent {
- return self
+ var tabContent = self
+ tabContent.badge = Text(resource)
+ return tabContent
}
- @available(*, unavailable)
public func badge(_ label: String) -> some TabContent {
- return self
+ var tabContent = self
+ tabContent.badge = Text(label)
+ return tabContent
}
@available(*, unavailable)
diff --git a/Sources/SkipUI/SkipUI/View/AdditionalViewModifiers.swift b/Sources/SkipUI/SkipUI/View/AdditionalViewModifiers.swift
index c4bbcb31..3e69dd90 100644
--- a/Sources/SkipUI/SkipUI/View/AdditionalViewModifiers.swift
+++ b/Sources/SkipUI/SkipUI/View/AdditionalViewModifiers.swift
@@ -1487,6 +1487,30 @@ final class AspectRatioModifier: RenderModifier {
}
}
+final class BadgeModifier: RenderModifier {
+ let badge: Text?
+ let prominence: BadgeProminence?
+
+ init(badge: Text? = nil, prominence: BadgeProminence? = nil) {
+ self.badge = badge
+ self.prominence = prominence
+ super.init()
+ }
+
+ static func combined(for renderable: Renderable) -> BadgeModifier {
+ var badge: Text? = nil
+ var prominence: BadgeProminence? = nil
+ renderable.forEachModifier {
+ if let badgeModifier = $0 as? BadgeModifier {
+ badge = badge ?? badgeModifier.badge
+ prominence = prominence ?? badgeModifier.prominence
+ }
+ return nil
+ }
+ return BadgeModifier(badge: badge, prominence: prominence)
+ }
+}
+
final class DisabledModifier: EnvironmentModifier {
let disabled: Bool