|
|
@@ -6,11 +6,10 @@ struct AppTileView: View {
|
|
|
let onTap: () -> Void
|
|
|
|
|
|
@State private var isHovering = false
|
|
|
- @State private var isPressing = false
|
|
|
@AppStorage("pinnedTileIDsData") private var pinnedTileIDsData = ""
|
|
|
@AppStorage("statusBarAppIDsData") private var statusBarAppIDsData = ""
|
|
|
@AppStorage("widgetAppIDsData") private var widgetAppIDsData = ""
|
|
|
- private let tileCornerRadius: CGFloat = 14
|
|
|
+ private let tileCornerRadius: CGFloat = 16
|
|
|
|
|
|
var body: some View {
|
|
|
ZStack(alignment: .topTrailing) {
|
|
|
@@ -79,85 +78,47 @@ struct AppTileView: View {
|
|
|
}
|
|
|
.onHover { hovering in
|
|
|
isHovering = hovering
|
|
|
- if !hovering {
|
|
|
- isPressing = false
|
|
|
- }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private var tileContent: some View {
|
|
|
- VStack(spacing: 9) {
|
|
|
+ VStack(spacing: 8) {
|
|
|
AppIconView(
|
|
|
app: app,
|
|
|
size: 48,
|
|
|
showAppBackground: false,
|
|
|
iconPaddingFactor: 0.07
|
|
|
)
|
|
|
- .shadow(
|
|
|
- color: .black.opacity(isHovering ? 0.24 : 0.06),
|
|
|
- radius: isHovering ? 12 : 5,
|
|
|
- x: 0,
|
|
|
- y: isHovering ? 8 : 2
|
|
|
- )
|
|
|
- .scaleEffect(isPressing ? 0.97 : (isHovering ? 1.02 : 1.0))
|
|
|
- .offset(y: isHovering ? -0.5 : 0)
|
|
|
+ // Subtle depth like an app-store tile.
|
|
|
+ .shadow(color: .black.opacity(0.14), radius: 8, x: 0, y: 4)
|
|
|
|
|
|
Text(app.name)
|
|
|
- .font(.system(size: 13, weight: .medium))
|
|
|
- .foregroundStyle(.white.opacity(isHovering ? 1.0 : 0.95))
|
|
|
+ .font(.system(size: 14, weight: .semibold))
|
|
|
+ .foregroundStyle(.white.opacity(0.95))
|
|
|
.lineLimit(1)
|
|
|
.truncationMode(.tail)
|
|
|
.frame(maxWidth: .infinity)
|
|
|
- .offset(y: isHovering ? -0.25 : 0)
|
|
|
}
|
|
|
.padding(.horizontal, 10)
|
|
|
.padding(.vertical, 12)
|
|
|
.background(
|
|
|
RoundedRectangle(cornerRadius: tileCornerRadius, style: .continuous)
|
|
|
- .fill(Color.white.opacity(isHovering ? 0.07 : 0.02))
|
|
|
- )
|
|
|
- .overlay(
|
|
|
- RoundedRectangle(cornerRadius: tileCornerRadius, style: .continuous)
|
|
|
- .fill(RadialGradient(
|
|
|
- colors: [
|
|
|
- Color.white.opacity(isHovering ? 0.16 : 0.06),
|
|
|
- .clear
|
|
|
- ],
|
|
|
- center: .top,
|
|
|
- startRadius: 12,
|
|
|
- endRadius: 100
|
|
|
- ))
|
|
|
- .allowsHitTesting(false)
|
|
|
- )
|
|
|
- .overlay(
|
|
|
- RoundedRectangle(cornerRadius: tileCornerRadius, style: .continuous)
|
|
|
- .stroke(
|
|
|
- isHovering ? Color.white.opacity(0.28) : Color.white.opacity(0.10),
|
|
|
- lineWidth: isHovering ? 1.2 : 1
|
|
|
- )
|
|
|
+ .fill(Color.white.opacity(isHovering ? 0.06 : 0.025))
|
|
|
)
|
|
|
.overlay(
|
|
|
RoundedRectangle(cornerRadius: tileCornerRadius, style: .continuous)
|
|
|
- .inset(by: 1)
|
|
|
- .stroke(
|
|
|
- isHovering ? Color.white.opacity(0.16) : Color.white.opacity(0.04),
|
|
|
- lineWidth: 1
|
|
|
- )
|
|
|
- .allowsHitTesting(false)
|
|
|
+ .stroke(Color.white.opacity(isHovering ? 0.18 : 0.08), lineWidth: 1)
|
|
|
)
|
|
|
- .scaleEffect(isPressing ? 0.99 : (isHovering ? 1.02 : 1.0))
|
|
|
- .offset(y: isPressing ? 0 : (isHovering ? -1 : 0))
|
|
|
.shadow(
|
|
|
- color: .black.opacity(isHovering ? 0.22 : 0),
|
|
|
- radius: isHovering ? 18 : 0,
|
|
|
+ color: .black.opacity(isHovering ? 0.18 : 0.10),
|
|
|
+ radius: isHovering ? 16 : 10,
|
|
|
x: 0,
|
|
|
- y: isHovering ? 12 : 0
|
|
|
+ y: isHovering ? 10 : 6
|
|
|
)
|
|
|
// Ensure tiles look consistent across the adaptive grid.
|
|
|
.frame(maxWidth: .infinity, minHeight: 94)
|
|
|
.contentShape(RoundedRectangle(cornerRadius: tileCornerRadius, style: .continuous))
|
|
|
- .animation(.spring(response: 0.24, dampingFraction: 0.88), value: isHovering)
|
|
|
- .animation(.easeOut(duration: 0.12), value: isPressing)
|
|
|
+ .animation(.easeOut(duration: 0.12), value: isHovering)
|
|
|
}
|
|
|
|
|
|
private var isPinned: Bool {
|