From 8319aa8ca97976810ba0b0e20cd293fba40f4711 Mon Sep 17 00:00:00 2001 From: Qiu Yuzhou Date: Thu, 15 Oct 2026 19:36:45 +0800 Subject: [PATCH] fix(servers): separate detail actions into two rows --- .../UI/Servers/CatalogNodeDetailView.swift | 2 +- .../App/UI/Servers/NodeDetailView.swift | 30 +++++++++++++++++-- .../App/UI/Servers/ServersView.swift | 6 +++- 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/ShadowsocksX-NG2/App/UI/Servers/CatalogNodeDetailView.swift b/ShadowsocksX-NG2/App/UI/Servers/CatalogNodeDetailView.swift index 65843701..b79f151a 100644 --- a/ShadowsocksX-NG2/App/UI/Servers/CatalogNodeDetailView.swift +++ b/ShadowsocksX-NG2/App/UI/Servers/CatalogNodeDetailView.swift @@ -32,7 +32,6 @@ struct CatalogNodeDetailView: View { } } } - .buttonStyle(.borderedProminent) .disabled(!(eligibility?.canActivate ?? false) || activation.isPending) if let subscription = subscriptionSummary(for: node) { Button( @@ -44,6 +43,7 @@ struct CatalogNodeDetailView: View { } .disabled(workflow.refreshingSubscriptionIDs.contains(subscription.id)) } + } secondaryActions: { if let module = workflow.connectivity { ConnectivityTestButton(module: module, node: node) } diff --git a/ShadowsocksX-NG2/App/UI/Servers/NodeDetailView.swift b/ShadowsocksX-NG2/App/UI/Servers/NodeDetailView.swift index 3b88226c..8d2be9ea 100644 --- a/ShadowsocksX-NG2/App/UI/Servers/NodeDetailView.swift +++ b/ShadowsocksX-NG2/App/UI/Servers/NodeDetailView.swift @@ -1,9 +1,12 @@ import SwiftUI /// 只接受展示值与外部动作;不查询节点类型、工作流或凭据。 -struct NodeDetailView: View { +struct NodeDetailView< + PrimaryActions: View, SecondaryActions: View, Feedback: View, Supplement: View +>: View { let detail: NodeDetailPresentation - @ViewBuilder let actions: Actions + @ViewBuilder let primaryActions: PrimaryActions + @ViewBuilder let secondaryActions: SecondaryActions @ViewBuilder let feedback: Feedback @ViewBuilder let supplement: Supplement @@ -26,7 +29,19 @@ struct NodeDetailView: View { .textSelection(.enabled) } } - HStack(spacing: 12) { actions } + VStack(alignment: .leading, spacing: 8) { + HStack(spacing: 12) { primaryActions } + .buttonStyle(.borderedProminent) + HStack(spacing: 12) { secondaryActions } + .buttonStyle(.bordered) + } + .fixedSize(horizontal: true, vertical: true) + .background { + GeometryReader { geometry in + Color.clear.preference( + key: NodeDetailMinimumWidth.self, value: geometry.size.width + 32) + } + } feedback } Divider() @@ -58,3 +73,12 @@ struct NodeDetailView: View { } } } + +/// 操作区按完整文字测量;包含详情的左右内边距,供外层分栏限制拖动宽度。 +struct NodeDetailMinimumWidth: PreferenceKey { + static let defaultValue: CGFloat = 320 + + static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { + value = max(value, nextValue()) + } +} diff --git a/ShadowsocksX-NG2/App/UI/Servers/ServersView.swift b/ShadowsocksX-NG2/App/UI/Servers/ServersView.swift index 8af0e065..9cf5f5bd 100644 --- a/ShadowsocksX-NG2/App/UI/Servers/ServersView.swift +++ b/ShadowsocksX-NG2/App/UI/Servers/ServersView.swift @@ -33,6 +33,7 @@ struct ServersView: View { @State private var subscriptionEditTarget: SubscriptionSummary? @State private var deleteTarget: DeleteTarget? @State private var dropState = ServerTableDropState() + @State private var detailMinimumWidth: CGFloat = NodeDetailMinimumWidth.defaultValue @FocusState private var isServerTableFocused: Bool // 分享 popover 打开时冻结载荷、顶部资料与建议文件名。 @State var shareContext: ShareContext? @@ -57,7 +58,10 @@ struct ServersView: View { ZStack(alignment: .topLeading) { serverDetailPane } - .frame(minWidth: 320, maxWidth: .infinity, maxHeight: .infinity) + .frame(minWidth: detailMinimumWidth, maxWidth: .infinity, maxHeight: .infinity) + .onPreferenceChange(NodeDetailMinimumWidth.self) { + detailMinimumWidth = max(NodeDetailMinimumWidth.defaultValue, $0) + } } .frame(minHeight: 420) .sheet(