Skip to content

Commit 9732544

Browse files
committed
adapt dark transparent spotlight background
1 parent cc2b563 commit 9732544

File tree

8 files changed

+134
-15
lines changed

8 files changed

+134
-15
lines changed

keyboard/KeyboardViewController.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ class KeyboardViewController: UIInputViewController, FcitxProtocol {
5757
addChild(hostingController)
5858

5959
hostingController.view.translatesAutoresizingMaskIntoConstraints = false
60+
61+
// Spotlight shows that system keyboard has transparent background.
62+
hostingController.view.backgroundColor = .clear
63+
view.backgroundColor = .clear
64+
6065
view.addSubview(hostingController.view)
6166

6267
NSLayoutConstraint.activate([

uipanel/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
add_library(KeyboardUI STATIC
22
keyboardui.swift
33
VirtualKeyboard.swift
4+
Color.swift
45
ContextMenu.swift
56
Key.swift
67
Bubble.swift
8+
Shadow.swift
79
KeyModifier.swift
810
Keyboard.swift
911
Candidate.swift

uipanel/Color.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import SwiftUI
2+
3+
extension Color {
4+
private func toRGBA() -> (r: CGFloat, g: CGFloat, b: CGFloat, a: CGFloat)? {
5+
let nativeColor = UIColor(self)
6+
var r: CGFloat = 0
7+
var g: CGFloat = 0
8+
var b: CGFloat = 0
9+
var a: CGFloat = 0
10+
guard nativeColor.getRed(&r, green: &g, blue: &b, alpha: &a) else {
11+
return nil
12+
}
13+
return (r, g, b, a)
14+
}
15+
16+
func blend(with background: Color) -> Color {
17+
guard let fg = self.toRGBA(),
18+
let bg = background.toRGBA()
19+
else {
20+
return self
21+
}
22+
23+
let r = (fg.r * fg.a + bg.r * (1 - fg.a))
24+
let g = (fg.g * fg.a + bg.g * (1 - fg.a))
25+
let b = (fg.b * fg.a + bg.b * (1 - fg.a))
26+
27+
return Color(.sRGB, red: r, green: g, blue: b, opacity: 1)
28+
}
29+
}

uipanel/ContextMenu.swift

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import SwiftUI
22

3+
func getBackground(_ colorScheme: ColorScheme) -> Color {
4+
return colorScheme == .dark ? darkBackground : lightBackground
5+
}
6+
37
struct MenuItem: Identifiable {
48
let id = UUID()
59
let text: String
@@ -39,17 +43,20 @@ struct ContextMenuOverlay: View {
3943
}
4044
}
4145
}
46+
47+
let background = getNormalBackground(colorScheme).blend(with: getBackground(colorScheme))
48+
4249
// If we wrap with ScrollView when not needed, the height will be extended.
4350
if hasScroll {
4451
ScrollView {
45-
menu.background(getNormalBackground(colorScheme))
52+
menu.background(background)
4653
}.cornerRadius(8)
4754
.shadow(radius: 4)
4855
.position(adjustedPosition)
4956
} else {
5057
menu.background(
5158
GeometryReader { geometry in
52-
getNormalBackground(colorScheme)
59+
background
5360
.onAppear {
5461
menuSize = geometry.size
5562
adjustedPosition = adjustPosition()

uipanel/KeyModifier.swift

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@ private func getSwipeDirection(_ dx: CGFloat, _ dy: CGFloat) -> SwipeDirection {
2020
}
2121

2222
private func clearBubble() {
23-
virtualKeyboardView.setBubble(0, 0, 0, 0, .clear, .clear, nil)
23+
virtualKeyboardView.setBubble(0, 0, 0, 0, .clear, .light, .clear, nil)
2424
}
2525

2626
struct KeyModifier: ViewModifier {
27+
@Environment(\.colorScheme) var colorScheme
28+
2729
let threshold: CGFloat = 30
2830
let stepSize: CGFloat = 15
2931

@@ -60,11 +62,14 @@ struct KeyModifier: ViewModifier {
6062
} else {
6163
content
6264
}
63-
}.frame(width: width - hMargin, height: height - rowGap)
65+
}.frame(width: width - hMargin, height: height - vMargin)
6466
.background(isPressed ? pressedBackground : background)
6567
.cornerRadius(radius)
6668
.foregroundColor(isPressed ? pressedForeground : foreground)
67-
.shadow(color: shadow, radius: 0, x: 0, y: 1)
69+
.overlay(
70+
ShadowView(width: width - hMargin, height: 1, radius: radius, color: shadow)
71+
.offset(y: (height - vMargin - radius + 1) / 2)
72+
)
6873
.condition(topRight != nil) {
6974
$0.overlay(
7075
// padding right so that / doesn't overflow
@@ -89,7 +94,9 @@ struct KeyModifier: ViewModifier {
8994
lastLocation = value.startLocation.x
9095

9196
virtualKeyboardView.setBubble(
92-
bubbleX, bubbleY, bubbleWidth, bubbleHeight, background, shadow, bubbleLabel)
97+
bubbleX, bubbleY, bubbleWidth, bubbleHeight,
98+
background, colorScheme, shadow,
99+
bubbleLabel)
93100

94101
// Schedule long press that can be interrupted by move.
95102
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
@@ -113,7 +120,8 @@ struct KeyModifier: ViewModifier {
113120
}
114121
if getSwipeDirection(dx, dy) == .up {
115122
virtualKeyboardView.setBubble(
116-
bubbleX, bubbleY, bubbleWidth, bubbleHeight, background, shadow, swipeUpLabel)
123+
bubbleX, bubbleY, bubbleWidth, bubbleHeight, background, colorScheme,
124+
shadow, swipeUpLabel)
117125
} else {
118126
clearBubble()
119127
}

uipanel/Shadow.swift

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import SwiftUI
2+
3+
// |\_______/|
4+
// | |
5+
// \_______/
6+
7+
// Put it below a key that needs shadow.
8+
// Needed because .shadow affects the entire button's transparency.
9+
10+
struct ShadowShape: Shape {
11+
let r: CGFloat
12+
13+
func path(in rect: CGRect) -> Path {
14+
var path = Path()
15+
let w = rect.width
16+
let h = rect.height
17+
18+
path.move(to: CGPoint(x: 0, y: h - r))
19+
path.addArc(
20+
center: CGPoint(x: r, y: h - r),
21+
radius: r,
22+
startAngle: .degrees(180),
23+
endAngle: .degrees(90),
24+
clockwise: true)
25+
path.addLine(to: CGPoint(x: w - r, y: h))
26+
path.addArc(
27+
center: CGPoint(x: w - r, y: h - r),
28+
radius: r,
29+
startAngle: .degrees(90),
30+
endAngle: .degrees(0),
31+
clockwise: true)
32+
path.addLine(to: CGPoint(x: w, y: 0))
33+
path.addArc(
34+
center: CGPoint(x: w - r, y: 0),
35+
radius: r,
36+
startAngle: .degrees(0),
37+
endAngle: .degrees(90),
38+
clockwise: false)
39+
path.addLine(to: CGPoint(x: r, y: r))
40+
path.addArc(
41+
center: CGPoint(x: r, y: 0),
42+
radius: r,
43+
startAngle: .degrees(90),
44+
endAngle: .degrees(180),
45+
clockwise: false)
46+
path.closeSubpath()
47+
return path
48+
}
49+
}
50+
51+
struct ShadowView: View {
52+
let width: CGFloat
53+
let height: CGFloat
54+
let radius: CGFloat
55+
let color: Color
56+
57+
var body: some View {
58+
ShadowShape(r: radius)
59+
.fill(color)
60+
.frame(width: width, height: height + radius)
61+
}
62+
}

uipanel/VirtualKeyboard.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ class ViewModel: ObservableObject {
5252
}
5353

5454
public struct VirtualKeyboardView: View {
55-
@Environment(\.colorScheme) var colorScheme
5655
@ObservedObject var viewModel = ViewModel()
5756

5857
public var body: some View {
@@ -97,7 +96,7 @@ public struct VirtualKeyboardView: View {
9796
bubbleShadow: viewModel.bubbleShadow,
9897
bubbleLabel: viewModel.bubbleLabel)
9998
}
100-
}.background(colorScheme == .dark ? darkBackground : lightBackground)
99+
}.background(transparent) // .clear will make gaps between candidates not scrollable.
101100
if viewModel.showMenu {
102101
ContextMenuOverlay(
103102
items: viewModel.menuItems,
@@ -228,13 +227,15 @@ public struct VirtualKeyboardView: View {
228227

229228
func setBubble(
230229
_ x: CGFloat, _ y: CGFloat, _ width: CGFloat, _ height: CGFloat, _ background: Color,
231-
_ shadow: Color, _ label: String?
230+
_ colorScheme: ColorScheme, _ shadow: Color, _ label: String?
232231
) {
233232
viewModel.bubbleX = x
234233
viewModel.bubbleY = y
235234
viewModel.bubbleWidth = width
236235
viewModel.bubbleHeight = height
237-
viewModel.bubbleBackground = background
236+
// This only guarantees same color with key when system background is pure
237+
// white/dark or key is opaque. In Spotlight, color discrepancy is expected.
238+
viewModel.bubbleBackground = background.blend(with: getBackground(colorScheme))
238239
viewModel.bubbleShadow = shadow
239240
viewModel.bubbleLabel = label
240241
}

uipanel/ui.swift

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ let candidateHorizontalPadding: CGFloat = 10
1313
let candidateVerticalPadding: CGFloat = 4
1414
let candidateGap: CGFloat = 4
1515

16+
let transparent = Color.black.opacity(0.001)
17+
1618
let lightBackground = Color(
1719
.sRGB, red: 210 / 255.0, green: 212 / 255.0, blue: 218 / 255.0, opacity: 1)
1820
let lightNormalBackground = Color.white
@@ -24,13 +26,16 @@ let lightHighlightBackground = Color(
2426

2527
let darkBackground = Color(
2628
.sRGB, red: 43 / 255.0, green: 43 / 255.0, blue: 43 / 255.0, opacity: 1)
29+
// Get 107, 107, 107 when blending with darkBackground.
2730
let darkNormalBackground = Color(
28-
.sRGB, red: 107 / 255.0, green: 107 / 255.0, blue: 107 / 255.0, opacity: 1)
31+
.sRGB, red: 1.0, green: 1.0, blue: 1.0, opacity: 0.3)
32+
// Get 70, 70, 70 when blending with darkBackground.
2933
let darkFunctionBackground = Color(
30-
.sRGB, red: 70 / 255.0, green: 70 / 255.0, blue: 70 / 255.0, opacity: 1)
31-
let darkShadow = Color(.sRGB, red: 37 / 255.0, green: 37 / 255.0, blue: 37 / 255.0, opacity: 1)
34+
.sRGB, red: 133 / 255.0, green: 133 / 255.0, blue: 133 / 255.0, opacity: 0.3)
35+
let darkShadow = Color(.sRGB, red: 16 / 255.0, green: 13 / 255.0, blue: 14 / 255.0, opacity: 0.3)
36+
// Get 67, 67, 70 when blending with darkBackground.
3237
let darkHighlightBackground = Color(
33-
.sRGB, red: 67 / 255.0, green: 67 / 255.0, blue: 70 / 255.0, opacity: 1)
38+
.sRGB, red: 123 / 255.0, green: 123 / 255.0, blue: 133 / 255.0, opacity: 0.3)
3439

3540
let disabledForeground = Color.gray
3641
let highlightForeground = Color.white

0 commit comments

Comments
 (0)