| | |
| | | |
| | | private func layoutSpaceLaneSegmentContents(_ segment: SpaceLaneSegmentLayers) { |
| | | let bounds = segment.containerLayer.bounds |
| | | let splitAppNames = splitViewAppNames(for: segment.space) |
| | | let isSplitView = splitAppNames.count >= 2 |
| | | segment.labelLayer.frame = CGRect(x: 10, y: bounds.height - 29, width: max(1, bounds.width - 48), height: 20) |
| | | segment.countLayer.frame = CGRect(x: bounds.width - 34, y: bounds.height - 29, width: 24, height: 20) |
| | | segment.countLayer.string = isSplitView ? "" : "\(segment.space.windowCount)" |
| | | segment.countLayer.isHidden = isSplitView |
| | | if segment.space.type == .fullscreen { |
| | | let markerFrame = fullscreenMarkerFrame(in: bounds) |
| | | let splitAppNames = splitViewAppNames(for: segment.space) |
| | | segment.appLayer.isHidden = splitAppNames.count >= 2 |
| | | if splitAppNames.count >= 2 { |
| | | segment.appLayer.isHidden = isSplitView |
| | | if isSplitView { |
| | | let labelY = markerFrame.midY - 8.5 |
| | | let halfWidth = markerFrame.width / 2 |
| | | for (index, splitAppLayer) in segment.splitAppLayers.enumerated() { |
| | |
| | | layoutFullscreenMarkerLayer( |
| | | fullscreenMarkerLayer, |
| | | in: bounds, |
| | | isSplitView: splitViewAppNames(for: segment.space).count >= 2 |
| | | isSplitView: isSplitView |
| | | ) |
| | | } |
| | | |
| | |
| | | |
| | | let bounds = shapeLayer.bounds |
| | | if isSplitView { |
| | | let insetBounds = bounds.insetBy(dx: 0.6, dy: 0.6) |
| | | let radius = min(7, max(5, insetBounds.height * 0.22)) |
| | | let path = CGMutablePath() |
| | | path.addRoundedRect( |
| | | in: insetBounds, |
| | | cornerWidth: radius, |
| | | cornerHeight: radius |
| | | ) |
| | | path.move(to: CGPoint(x: bounds.midX, y: insetBounds.minY + 1.5)) |
| | | path.addLine(to: CGPoint(x: bounds.midX, y: insetBounds.maxY - 1.5)) |
| | | path.move(to: CGPoint(x: bounds.midX, y: bounds.minY)) |
| | | path.addLine(to: CGPoint(x: bounds.midX, y: bounds.maxY)) |
| | | shapeLayer.path = path |
| | | shapeLayer.lineCap = .butt |
| | | return |
| | | } |
| | | |
| | |
| | | path.addLine(to: CGPoint(x: 0, y: bounds.maxY - cornerLength)) |
| | | |
| | | shapeLayer.path = path |
| | | shapeLayer.lineCap = .round |
| | | } |
| | | |
| | | private func fullscreenMarkerFrame(in segmentBounds: CGRect) -> CGRect { |
| | |
| | | markerLayer: segment.fullscreenMarkerLayer, |
| | | splitAppNames: splitAppNames |
| | | ), |
| | | "fullscreenMarkerPathStyle": fullscreenMarkerPathStyle( |
| | | markerLayer: segment.fullscreenMarkerLayer, |
| | | splitAppNames: splitAppNames |
| | | ), |
| | | "fullscreenMarkerFrame": fullscreenMarkerFrame.map { dictionary(from: $0) } ?? NSNull(), |
| | | "fullscreenMarkerCenterRatio": fullscreenMarkerCenterRatio ?? NSNull(), |
| | | "splitViewAppNames": splitAppNames, |
| | |
| | | "windowBlockColors": segment.windowBlocks.map { colorDictionary(from: $0.backgroundColor) }, |
| | | "windowBlockFrames": segment.windowBlocks.map { dictionary(from: $0.frame) }, |
| | | "labelColor": colorDictionary(from: segment.labelLayer.foregroundColor), |
| | | "countText": string(from: segment.countLayer.string), |
| | | "countVisible": !segment.countLayer.isHidden && segment.countLayer.opacity > 0, |
| | | "countColor": colorDictionary(from: segment.countLayer.foregroundColor), |
| | | "appColor": colorDictionary(from: segment.appLayer.foregroundColor), |
| | | "fullscreenMarkerStrokeColor": colorDictionary( |
| | |
| | | private func fullscreenMarkerKind(markerLayer: CALayer?, splitAppNames: [String]) -> String { |
| | | guard markerLayer != nil else { return "none" } |
| | | return splitAppNames.count >= 2 ? "splitViewPair" : "cornerBracket" |
| | | } |
| | | |
| | | private func fullscreenMarkerPathStyle(markerLayer: CALayer?, splitAppNames: [String]) -> String { |
| | | guard markerLayer != nil else { return "none" } |
| | | return splitAppNames.count >= 2 ? "centerDividerOnly" : "cornerBracket" |
| | | } |
| | | |
| | | private func visualStates(for space: QuickSwitchSpaceViewModel) -> [String] { |
| | |
| | | ] |
| | | } |
| | | |
| | | private func string(from textLayerValue: Any?) -> String { |
| | | if let string = textLayerValue as? String { |
| | | return string |
| | | } |
| | | if let attributedString = textLayerValue as? NSAttributedString { |
| | | return attributedString.string |
| | | } |
| | | return "" |
| | | } |
| | | |
| | | private func colorDictionary(from color: CGColor?) -> [String: Double] { |
| | | guard let color, |
| | | let nsColor = NSColor(cgColor: color)?.usingColorSpace(.deviceRGB) |