|
|
@@ -671,7 +671,28 @@ private struct PanelAppRowView: View {
|
|
|
}
|
|
|
|
|
|
var body: some View {
|
|
|
- ZStack(alignment: .topTrailing) {
|
|
|
+ ZStack {
|
|
|
+ RoundedRectangle(cornerRadius: 12, style: .continuous)
|
|
|
+ .fill(Color.white.opacity(isHovering ? 0.18 : 0))
|
|
|
+ .overlay(
|
|
|
+ RoundedRectangle(cornerRadius: 12, style: .continuous)
|
|
|
+ .stroke(
|
|
|
+ LinearGradient(
|
|
|
+ colors: [
|
|
|
+ Color.white.opacity(isHovering ? 0.38 : 0),
|
|
|
+ Color.white.opacity(isHovering ? 0.14 : 0),
|
|
|
+ ],
|
|
|
+ startPoint: .topLeading,
|
|
|
+ endPoint: .bottomTrailing
|
|
|
+ ),
|
|
|
+ lineWidth: 1
|
|
|
+ )
|
|
|
+ )
|
|
|
+ .shadow(color: Color.black.opacity(isHovering ? 0.35 : 0), radius: isHovering ? 14 : 0, x: 0, y: 4)
|
|
|
+ .animation(.easeOut(duration: 0.14), value: isHovering)
|
|
|
+ .allowsHitTesting(false)
|
|
|
+
|
|
|
+ ZStack(alignment: .trailing) {
|
|
|
HStack(spacing: 14) {
|
|
|
AppIconGlyph(app: app)
|
|
|
.scaleEffect(isHovering ? 1.05 : 1.0, anchor: .leading)
|
|
|
@@ -707,18 +728,16 @@ private struct PanelAppRowView: View {
|
|
|
}
|
|
|
|
|
|
if shouldShowQuickActions {
|
|
|
- HStack(spacing: 8) {
|
|
|
+ HStack(spacing: 10) {
|
|
|
ForEach(quickActions) { action in
|
|
|
PanelQuickActionButton(
|
|
|
action: action,
|
|
|
- isRowHovering: isHovering,
|
|
|
onTap: { openWidgetAction(action) }
|
|
|
)
|
|
|
}
|
|
|
}
|
|
|
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .trailing)
|
|
|
.padding(.trailing, 34)
|
|
|
- .padding(.top, 18)
|
|
|
}
|
|
|
|
|
|
if !app.isCreateNew {
|
|
|
@@ -755,12 +774,12 @@ private struct PanelAppRowView: View {
|
|
|
.menuStyle(.borderlessButton)
|
|
|
.modifier(HideMenuIndicatorIfAvailable())
|
|
|
.padding(8)
|
|
|
- // Match the quick-action icon row vertical alignment.
|
|
|
- .offset(y: shouldShowQuickActions ? 18 : 0)
|
|
|
.opacity(isHovering ? 1 : 0.0)
|
|
|
.animation(.easeOut(duration: 0.12), value: isHovering)
|
|
|
}
|
|
|
+ }
|
|
|
}
|
|
|
+ .frame(maxWidth: .infinity, alignment: .leading)
|
|
|
.onHover { hovering in
|
|
|
isHovering = hovering
|
|
|
}
|
|
|
@@ -878,21 +897,22 @@ private struct PanelAppRowView: View {
|
|
|
}
|
|
|
|
|
|
private struct PanelQuickActionButton: View {
|
|
|
+ private static let iconPointSize: CGFloat = 15
|
|
|
+ private static let circleDiameter: CGFloat = 32
|
|
|
+
|
|
|
let action: WidgetAction
|
|
|
- let isRowHovering: Bool
|
|
|
let onTap: () -> Void
|
|
|
|
|
|
@State private var isHovering = false
|
|
|
|
|
|
- // Only highlight the hovered icon (not the whole row).
|
|
|
private var highlight: Bool { isHovering }
|
|
|
|
|
|
var body: some View {
|
|
|
Button(action: onTap) {
|
|
|
Image(systemName: action.systemImage)
|
|
|
- .font(.system(size: 12, weight: .semibold))
|
|
|
+ .font(.system(size: Self.iconPointSize, weight: .semibold))
|
|
|
.foregroundStyle(.white.opacity(0.82))
|
|
|
- .frame(width: 24, height: 24)
|
|
|
+ .frame(width: Self.circleDiameter, height: Self.circleDiameter)
|
|
|
.background(
|
|
|
Circle()
|
|
|
.fill(Color.black.opacity(highlight ? 0.48 : 0.22))
|
|
|
@@ -904,8 +924,6 @@ private struct PanelQuickActionButton: View {
|
|
|
.shadow(color: .black.opacity(highlight ? 0.28 : 0.20), radius: highlight ? 12 : 8, x: 0, y: 4)
|
|
|
}
|
|
|
.buttonStyle(.plain)
|
|
|
- // Keep hover effects to highlight/shadow only (no scaling) so icons
|
|
|
- // don't push outside the right edge.
|
|
|
.animation(.spring(response: 0.28, dampingFraction: 0.85), value: highlight)
|
|
|
.overlay(alignment: .top) {
|
|
|
Text(action.title)
|
|
|
@@ -924,8 +942,7 @@ private struct PanelQuickActionButton: View {
|
|
|
.stroke(Color.white.opacity(0.12), lineWidth: 1)
|
|
|
)
|
|
|
.shadow(color: .black.opacity(0.32), radius: 7, x: 0, y: 3)
|
|
|
- // Keep the title close to the icon.
|
|
|
- .offset(y: -22)
|
|
|
+ .offset(y: -(Self.circleDiameter / 2 + 14))
|
|
|
.opacity(isHovering ? 1 : 0)
|
|
|
.scaleEffect(isHovering ? 1 : 0.88, anchor: .bottom)
|
|
|
.offset(y: isHovering ? 0 : 5)
|