From 165963de84732045c4624ea7402063cca20547c9 Mon Sep 17 00:00:00 2001 From: gentpan Date: Wed, 16 Sep 2026 19:48:18 +0500 Subject: [PATCH] =?UTF-8?q?=E5=BC=B9=E7=AA=97=E9=AB=98=E5=BA=A6=E9=9A=8F?= =?UTF-8?q?=E5=86=85=E5=AE=B9=E8=87=AA=E5=8A=A8=E8=B0=83=E6=95=B4=EF=BC=9A?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=88=B0=E8=BE=BE=E5=90=8E=E5=8F=98=E9=AB=98?= =?UTF-8?q?=E4=B8=8D=E5=86=8D=E9=9C=B2=E5=87=BA=E6=BB=9A=E5=8A=A8=E6=9D=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 弹窗打开时按平铺布局测一次高度,之后公网 IP、归属地等数据陆续到达,内容变高(实测 860 → 911pt)而窗口不变,底部露出一小截滚动条。 - PageScroll 在弹窗里用 onGeometryChange 记下滚动内容高度与可视高度,差值通过 reportPopoverOverflow 环境值交给面板 - StatusPanel.adjustHeight(by:):按差值调整窗口高度、顶边不动,上限为菜单栏下方到屏幕底边的可用高度,屏幕放不下时才保留滚动 - 打开时的平铺测量保留作为起始高度;展开 / 收起区块、数据刷新都会自动校正 --- CHANGELOG.md | 4 +++ .../OpenStatsUI/AppKit/StatusPanel.swift | 30 +++++++++++++++---- .../OpenStatsUI/Design/Components.swift | 26 ++++++++++++++-- README.md | 8 +++-- README.zh-CN.md | 8 +++-- project.yml | 2 +- 6 files changed, 66 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc40792..c22d493 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ ### 2026-09-16 +#### 修复 + +- 网络弹窗偶尔差一点放不下、露出一小截滚动条:弹窗打开时按当时的内容算高度,之后公网 IP、归属地等数据陆续到达,内容变高了窗口却不跟着变。现在内容一变高或变矮,窗口高度随之调整到正好放下,只有屏幕放不下时才出现滚动条;展开、收起区块时同样适用。 + #### 样式 - 主窗口在 macOS 26 上改用液态玻璃:侧边栏是一块浮在窗口里的玻璃面板,红绿灯按钮落在面板里,选中项垫一块淡蓝色圆角底并随切换滑动;顶栏右侧“在菜单栏显示”一组收进玻璃胶囊。主按钮是蓝色玻璃胶囊、次要按钮与图标按钮是透明玻璃,风扇模式等快捷切换是玻璃胶囊,开关换成系统开关(拖动时滑块变成玻璃)。设置分组与数据卡片保持实色,与系统设置一致。macOS 14、15 上保持原来的样式。 diff --git a/Packages/OpenStatsKit/Sources/OpenStatsUI/AppKit/StatusPanel.swift b/Packages/OpenStatsKit/Sources/OpenStatsUI/AppKit/StatusPanel.swift index fc62cc7..7e1f068 100644 --- a/Packages/OpenStatsKit/Sources/OpenStatsUI/AppKit/StatusPanel.swift +++ b/Packages/OpenStatsKit/Sources/OpenStatsUI/AppKit/StatusPanel.swift @@ -16,19 +16,24 @@ final class StatusPanel: NSPanel { private var lastDismissal = Date.distantPast /// 面板隐藏时不保留 SwiftUI 视图,避免后台随数据刷新重绘 - private let makeContent: () -> NSView + private var makeContent: (() -> NSView)? /// 平铺布局(不含滚动容器)的视图,只用来测量内容的自然高度 private let makeMeasuringContent: () -> NSView private let width: CGFloat init(width: CGFloat, content: @escaping () -> Content, measuring: @escaping () -> Measuring) { self.width = width - makeContent = { NSHostingView(rootView: content()) } makeMeasuringContent = { NSHostingView(rootView: measuring()) } super.init(contentRect: NSRect(x: 0, y: 0, width: width, height: DS.Size.panelMinHeight), styleMask: [.borderless, .nonactivatingPanel], backing: .buffered, defer: true) + // 打开时按平铺布局测出的高度只是起点:真实内容会随数据刷新变高变矮,由滚动区域上报差值再校正 + makeContent = { [weak self] in + NSHostingView(rootView: content().environment(\.reportPopoverOverflow) { overflow in + self?.adjustHeight(by: overflow) + }) + } isFloatingPanel = true level = .statusBar collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary, .transient, .ignoresCycle] @@ -56,7 +61,7 @@ final class StatusPanel: NSPanel { self.anchor = anchor anchorScreen = screen - let content = makeContent() + guard let content = makeContent?() else { return } setFrame(targetFrame(), display: false) content.frame = NSRect(origin: .zero, size: frame.size) contentView = PanelContainer.make(containing: content, cornerRadius: DS.Radius.xl) @@ -81,6 +86,15 @@ final class StatusPanel: NSPanel { invalidateShadow() } + /// 滚动内容比可视区域高出(或矮出)一截时调整窗口高度,顶边不动;屏幕放不下时停在最高处,由滚动条兜底 + func adjustHeight(by overflow: CGFloat) { + guard contentView != nil, abs(overflow) > 1 else { return } + let height = min(max(frame.height + overflow, DS.Size.panelMinHeight), availableHeight()) + guard abs(height - frame.height) > 1 else { return } + setFrame(NSRect(x: frame.minX, y: frame.maxY - height, width: width, height: height), display: true) + invalidateShadow() + } + func dismiss() { guard isVisible, !isPinned else { return } lastDismissal = Date() @@ -116,16 +130,22 @@ final class StatusPanel: NSPanel { let visible = (anchorScreen ?? NSScreen.main)?.visibleFrame ?? .zero let margin = DS.Space.s2 let top = min(anchor.minY - DS.Size.panelGap, visible.maxY) - let available = max(DS.Size.panelMinHeight, top - visible.minY - margin) let natural = makeMeasuringContent().fittingSize.height - let height = min(max(natural, DS.Size.panelMinHeight), available) + let height = min(max(natural, DS.Size.panelMinHeight), availableHeight()) var x = anchor.midX - width / 2 x = min(max(x, visible.minX + margin), visible.maxX - width - margin) return NSRect(x: x, y: top - height, width: width, height: height) } + /// 菜单栏下方到屏幕底边(留一点边距)能放下的最大高度 + private func availableHeight() -> CGFloat { + let visible = (anchorScreen ?? NSScreen.main)?.visibleFrame ?? .zero + let top = min(anchor.minY - DS.Size.panelGap, visible.maxY) + return max(DS.Size.panelMinHeight, top - visible.minY - DS.Space.s2) + } + // MARK: 事件 private func installMonitors() { diff --git a/Packages/OpenStatsKit/Sources/OpenStatsUI/Design/Components.swift b/Packages/OpenStatsKit/Sources/OpenStatsUI/Design/Components.swift index 718d092..631cf66 100644 --- a/Packages/OpenStatsKit/Sources/OpenStatsUI/Design/Components.swift +++ b/Packages/OpenStatsKit/Sources/OpenStatsUI/Design/Components.swift @@ -9,6 +9,8 @@ extension EnvironmentValues { @Entry var isDetailPage = false /// 菜单栏弹窗:区块去掉卡片底色,用细分隔线隔开,排得更紧凑 @Entry var isPopover = false + /// 菜单栏弹窗:滚动内容的实际高度比可视区域高出(负数为矮出)多少,交给面板调整窗口高度 + @Entry var reportPopoverOverflow: (@MainActor @Sendable (CGFloat) -> Void)? = nil } // MARK: - 卡片 @@ -851,7 +853,10 @@ struct DSSlider: View { struct PageScroll: View { @Environment(\.isSnapshot) private var isSnapshot @Environment(\.isPopover) private var isPopover + @Environment(\.reportPopoverOverflow) private var reportOverflow @ViewBuilder var content: Content + @State private var contentHeight: CGFloat = 0 + @State private var viewportHeight: CGFloat = 0 var body: some View { // 弹窗里区块之间靠分隔线隔开,不再额外留间距 @@ -863,10 +868,27 @@ struct PageScroll: View { stack } else { // 内容放得下时不回弹,避免点击时整页轻微抖动 - ScrollView { stack.overlayScrollers() } - .scrollBounceBehavior(.basedOnSize) + ScrollView { + stack + .overlayScrollers() + .onGeometryChange(for: CGFloat.self) { $0.size.height } action: { height in + contentHeight = height + report() + } + } + .scrollBounceBehavior(.basedOnSize) + .onGeometryChange(for: CGFloat.self) { $0.size.height } action: { height in + viewportHeight = height + report() + } } } + + /// 弹窗里内容高度一变(数据刷新、展开收起),就让面板把窗口调到正好放下;屏幕放不下时才出现滚动 + private func report() { + guard let reportOverflow, contentHeight > 0, viewportHeight > 0 else { return } + reportOverflow(contentHeight - viewportHeight) + } } // MARK: - 徽章 / 标签 diff --git a/README.md b/README.md index c6c80e1..9c7144b 100644 --- a/README.md +++ b/README.md @@ -59,10 +59,14 @@ system language by default; switch it in Settings → General → Language. -Latest release **0.3.2** (2026-09-16) · **6** changes in development · [full changelog](CHANGELOG.md) (kept in Chinese) +Latest release **0.3.2** (2026-09-16) · **7** changes in development · [full changelog](CHANGELOG.md) (kept in Chinese)
-2026-09-16 · Unreleased · 5 style · 1 added +2026-09-16 · Unreleased · 1 fixed · 5 style · 1 added + +**Fixed** + +- 网络弹窗偶尔差一点放不下、露出一小截滚动条:弹窗打开时按当时的内容算高度,之后公网 IP、归属地等数据陆续到达,内容变高了窗口却不跟着变。现在内容一变高或变矮,窗口高度随之调整到正好放下,只有屏幕放不下时才出现滚动条;展开、收起区块时同样适用。 **Style** diff --git a/README.zh-CN.md b/README.zh-CN.md index 056734d..f585840 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -55,10 +55,14 @@ CPU 不分性能核与能效核,部分功耗与频率读数可能不显示。 -最新版本 **0.3.2**(2026-09-16) · 开发中 **6** 项改动尚未发布 · [完整更新日志](CHANGELOG.md) +最新版本 **0.3.2**(2026-09-16) · 开发中 **7** 项改动尚未发布 · [完整更新日志](CHANGELOG.md)
-2026-09-16 · 未发布 · 样式 5 · 新增 1 +2026-09-16 · 未发布 · 修复 1 · 样式 5 · 新增 1 + +**修复** + +- 网络弹窗偶尔差一点放不下、露出一小截滚动条:弹窗打开时按当时的内容算高度,之后公网 IP、归属地等数据陆续到达,内容变高了窗口却不跟着变。现在内容一变高或变矮,窗口高度随之调整到正好放下,只有屏幕放不下时才出现滚动条;展开、收起区块时同样适用。 **样式** diff --git a/project.yml b/project.yml index ec9b343..cbe73ae 100644 --- a/project.yml +++ b/project.yml @@ -13,7 +13,7 @@ settings: SWIFT_VERSION: "6.0" MARKETING_VERSION: "0.3.2" # 构建号四位数字,每次 make build 加一;营销版本只在发布时用 Scripts/version.sh patch / minor 修改 - CURRENT_PROJECT_VERSION: "0078" + CURRENT_PROJECT_VERSION: "0081" MACOSX_DEPLOYMENT_TARGET: "14.0" # 本地开发使用 ad-hoc 签名;发布时改为 Developer ID(见 docs/PLAN.md) CODE_SIGN_STYLE: Manual