From a4af5d51e8fbb8a0e53f98aa9f9e238536aaba24 Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Fri, 12 Jun 2026 21:08:39 +0800
Subject: [PATCH] Render split view spaces with ordered app halves
---
C1.source/Sources/Aligner/QuickSwitchRootView.swift | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++----
1 files changed, 103 insertions(+), 9 deletions(-)
diff --git a/C1.source/Sources/Aligner/QuickSwitchRootView.swift b/C1.source/Sources/Aligner/QuickSwitchRootView.swift
index d3f89ae..4ea3319 100644
--- a/C1.source/Sources/Aligner/QuickSwitchRootView.swift
+++ b/C1.source/Sources/Aligner/QuickSwitchRootView.swift
@@ -226,6 +226,7 @@
let labelLayer: CATextLayer
let countLayer: CATextLayer
let appLayer: CATextLayer
+ let splitAppLayers: [CATextLayer]
let windowBlocks: [CALayer]
let fullscreenMarkerLayer: CALayer?
}
@@ -2540,6 +2541,15 @@
color: space.type == .fullscreen ? .labelColor : .secondaryLabelColor,
alignment: space.type == .fullscreen ? .center : .left
)
+ let splitAppLayers = splitViewAppNames(for: space).map { name in
+ makeTextLayer(
+ string: name,
+ fontSize: 12,
+ weight: .medium,
+ color: .labelColor,
+ alignment: .center
+ )
+ }
let windowBlocks = space.type == .fullscreen
? []
: makeWindowBlocks(count: min(space.windowCount, 5), space: space)
@@ -2550,6 +2560,9 @@
container.addSublayer(appLayer)
if let fullscreenMarkerLayer {
container.addSublayer(fullscreenMarkerLayer)
+ }
+ for splitAppLayer in splitAppLayers {
+ container.addSublayer(splitAppLayer)
}
for block in windowBlocks {
container.addSublayer(block)
@@ -2562,6 +2575,7 @@
labelLayer: labelLayer,
countLayer: countLayer,
appLayer: appLayer,
+ splitAppLayers: splitAppLayers,
windowBlocks: windowBlocks,
fullscreenMarkerLayer: fullscreenMarkerLayer
)
@@ -2925,6 +2939,9 @@
? primaryTextColor
: secondaryTextColor
).cgColor
+ for splitAppLayer in segment.splitAppLayers {
+ splitAppLayer.foregroundColor = primaryTextColor.cgColor
+ }
for block in segment.windowBlocks {
block.backgroundColor = (isLocked
@@ -3074,17 +3091,45 @@
segment.countLayer.frame = CGRect(x: bounds.width - 34, y: bounds.height - 29, width: 24, height: 20)
if segment.space.type == .fullscreen {
let markerFrame = fullscreenMarkerFrame(in: bounds)
- segment.appLayer.frame = CGRect(
- x: 8,
- y: markerFrame.midY - 8.5,
- width: max(1, bounds.width - 16),
- height: 17
- )
+ let splitAppNames = splitViewAppNames(for: segment.space)
+ segment.appLayer.isHidden = splitAppNames.count >= 2
+ if splitAppNames.count >= 2 {
+ let labelY = markerFrame.midY - 8.5
+ let halfWidth = markerFrame.width / 2
+ for (index, splitAppLayer) in segment.splitAppLayers.enumerated() {
+ let x = markerFrame.minX + CGFloat(index) * halfWidth
+ splitAppLayer.isHidden = false
+ splitAppLayer.frame = CGRect(
+ x: x + 4,
+ y: labelY,
+ width: max(1, halfWidth - 8),
+ height: 17
+ )
+ }
+ } else {
+ segment.appLayer.frame = CGRect(
+ x: 8,
+ y: markerFrame.midY - 8.5,
+ width: max(1, bounds.width - 16),
+ height: 17
+ )
+ for splitAppLayer in segment.splitAppLayers {
+ splitAppLayer.isHidden = true
+ }
+ }
} else {
+ segment.appLayer.isHidden = false
segment.appLayer.frame = CGRect(x: 10, y: 9, width: max(1, bounds.width - 20), height: 17)
+ for splitAppLayer in segment.splitAppLayers {
+ splitAppLayer.isHidden = true
+ }
}
if let fullscreenMarkerLayer = segment.fullscreenMarkerLayer {
- layoutFullscreenMarkerLayer(fullscreenMarkerLayer, in: bounds)
+ layoutFullscreenMarkerLayer(
+ fullscreenMarkerLayer,
+ in: bounds,
+ isSplitView: splitViewAppNames(for: segment.space).count >= 2
+ )
}
let blockGap: CGFloat = 5
@@ -4671,13 +4716,44 @@
return identifiers.joined(separator: " ")
}
- private func layoutFullscreenMarkerLayer(_ layer: CALayer, in segmentBounds: CGRect) {
+ private func splitViewAppNames(for space: QuickSwitchSpaceViewModel) -> [String] {
+ guard space.type == .fullscreen else { return [] }
+
+ let names = space.splitViewAppNames
+ .prefix(2)
+ .map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
+ .filter { !$0.isEmpty }
+
+ guard names.count >= 2 else { return [] }
+ return names
+ }
+
+ private func layoutFullscreenMarkerLayer(
+ _ layer: CALayer,
+ in segmentBounds: CGRect,
+ isSplitView: Bool = false
+ ) {
let markerFrame = fullscreenMarkerFrame(in: segmentBounds)
layer.frame = markerFrame
guard let shapeLayer = layer as? CAShapeLayer else { return }
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))
+ shapeLayer.path = path
+ return
+ }
+
let cornerLength = min(12, max(7, min(bounds.width, bounds.height) * 0.28))
let path = CGMutablePath()
@@ -5086,6 +5162,8 @@
let fullscreenMarkerCenterRatio = fullscreenMarkerFrame.map {
Double($0.midY / max(1, segment.containerLayer.bounds.height))
}
+ let splitAppNames = splitViewAppNames(for: space)
+ let splitAppLabelFrames = segment.splitAppLayers.map { dictionary(from: $0.frame) }
return [
"displayUUID": display.displayUUID,
"displayLabel": display.displayLabel,
@@ -5094,9 +5172,17 @@
"type": spaceTypeName(space.type),
"visualStates": visualStates(for: space),
"fullscreenMarkerVisible": segment.fullscreenMarkerLayer != nil,
- "fullscreenMarkerKind": segment.fullscreenMarkerLayer == nil ? "none" : "cornerBracket",
+ "fullscreenMarkerKind": fullscreenMarkerKind(
+ markerLayer: segment.fullscreenMarkerLayer,
+ splitAppNames: splitAppNames
+ ),
"fullscreenMarkerFrame": fullscreenMarkerFrame.map { dictionary(from: $0) } ?? NSNull(),
"fullscreenMarkerCenterRatio": fullscreenMarkerCenterRatio ?? NSNull(),
+ "splitViewAppNames": splitAppNames,
+ "splitViewLabelFrames": splitAppLabelFrames,
+ "splitViewLabelColors": segment.splitAppLayers.map {
+ colorDictionary(from: $0.foregroundColor)
+ },
"windowBlockCount": segment.windowBlocks.count,
"windowBlockColors": segment.windowBlocks.map { colorDictionary(from: $0.backgroundColor) },
"windowBlockFrames": segment.windowBlocks.map { dictionary(from: $0.frame) },
@@ -5140,6 +5226,11 @@
}
}
+ private func fullscreenMarkerKind(markerLayer: CALayer?, splitAppNames: [String]) -> String {
+ guard markerLayer != nil else { return "none" }
+ return splitAppNames.count >= 2 ? "splitViewPair" : "cornerBracket"
+ }
+
private func visualStates(for space: QuickSwitchSpaceViewModel) -> [String] {
var states: [String] = []
if space.isCurrent {
@@ -5163,6 +5254,9 @@
if space.type == .fullscreen {
states.append("fullscreen")
}
+ if splitViewAppNames(for: space).count >= 2 {
+ states.append("splitView")
+ }
return states
}
--
Gitblit v1.9.3