Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 2 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1463,16 +1463,8 @@ Support levels:
<td><code>.backgroundStyle</code></td>
</tr>
<tr>
<td>🟡</td>
<td>
<details>
<summary><code>.badge</code></summary>
<ul>
<li>Supported on <code>List</code> items</li>
<li>Not yet supported on <code>TabView</code></li>
</ul>
</details>
</td>
<td>✅</td>
<td><code>.badge</code></td>
</tr>
<tr>
<td>🟢</td>
Expand Down
24 changes: 0 additions & 24 deletions Sources/SkipUI/SkipUI/Containers/List.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
67 changes: 53 additions & 14 deletions Sources/SkipUI/SkipUI/Containers/TabView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -228,10 +231,16 @@ public struct TabView : View, Renderable {
let tabRenderables = EvaluateContent(context: tabContext)
let tabs: kotlin.collections.List<Tab?> = 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
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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 {
Expand All @@ -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 {
Expand Down Expand Up @@ -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<Renderable> {
Expand All @@ -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
}
Expand Down Expand Up @@ -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)
Expand Down
24 changes: 24 additions & 0 deletions Sources/SkipUI/SkipUI/View/AdditionalViewModifiers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down