diff --git a/Changelog.md b/Changelog.md index d7d38c0de..178cc0986 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,8 @@ ### 🐞 修复问题 +- macOS 托盘速率可能的样式错误 +
✨ 新增功能 @@ -9,5 +11,7 @@
🚀 优化改进 + - 关闭 autofill 弹出窗口 +
diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 30f69863d..02419b944 100755 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -129,6 +129,7 @@ objc2-app-kit = { version = "0.3.2", features = [ "NSFontDescriptor", "NSColor", "NSParagraphStyle", + "NSStringDrawing", "NSText", ] } diff --git a/src-tauri/src/utils/tray_speed.rs b/src-tauri/src/utils/tray_speed.rs index 1367ac7f4..364a6458b 100644 --- a/src-tauri/src/utils/tray_speed.rs +++ b/src-tauri/src/utils/tray_speed.rs @@ -11,22 +11,30 @@ use objc2::MainThreadMarker; use objc2::rc::Retained; use objc2::runtime::AnyObject; use objc2_app_kit::{ - NSBaselineOffsetAttributeName, NSColor, NSFont, NSFontAttributeName, NSFontWeightRegular, - NSForegroundColorAttributeName, NSMutableParagraphStyle, NSParagraphStyleAttributeName, NSStatusBarButton, - NSStatusItem, NSTextAlignment, + NSAttributedStringNSStringDrawing, NSBaselineOffsetAttributeName, NSColor, NSFont, NSFontAttributeName, + NSFontWeightRegular, NSForegroundColorAttributeName, NSLineBreakMode, NSMutableParagraphStyle, + NSParagraphStyleAttributeName, NSStatusBarButton, NSStatusItem, NSTextAlignment, }; use objc2_foundation::{NSAttributedString, NSDictionary, NSNumber, NSString}; /// 富文本渲染使用的字号(适配两行在托盘栏的高度) const TRAY_FONT_SIZE: f64 = 9.5; -/// 两行文本的行间距(负值可压缩两行高度,便于与图标纵向居中) -const TRAY_LINE_SPACING: f64 = -1.0; +/// 两行文本的固定行高,避免不同菜单栏高度/缩放下使用系统默认行高导致裁剪 +const TRAY_LINE_HEIGHT: f64 = 10.0; +/// 两行文本的行间距 +const TRAY_LINE_SPACING: f64 = 0.0; /// 两行文本整体行高倍数(用于进一步压缩文本块高度) const TRAY_LINE_HEIGHT_MULTIPLE: f64 = 1.00; /// 文本块段前偏移(用于将两行文本整体下移) -const TRAY_PARAGRAPH_SPACING_BEFORE: f64 = -5.0; -/// 文字基线偏移(负值向下移动,更容易与托盘图标垂直居中) -const TRAY_BASELINE_OFFSET: f64 = -4.0; +const TRAY_PARAGRAPH_SPACING_BEFORE: f64 = 0.0; +/// 文字基线偏移(负值向下移动,避免上行速率贴住菜单栏顶部) +const TRAY_BASELINE_OFFSET: f64 = -3.0; +/// Tauri tray-icon 将图标缩放为 18pt;这里额外预留图标、图文间距与系统内边距 +const TRAY_STATUS_ITEM_EXTRA_WIDTH: f64 = 30.0; +/// 典型 6 字符速率文本的最小宽度,避免 0B/s 等短文本让状态项反复收缩 +const TRAY_STATUS_ITEM_MIN_LENGTH: f64 = 58.0; +/// AppKit 的 NSVariableStatusItemLength。清空速率标题后恢复系统按图标自适应 +const NS_VARIABLE_STATUS_ITEM_LENGTH: f64 = -1.0; thread_local! { /// 托盘速率富文本属性字典(主线程缓存,避免每帧重建 ObjC 对象)。 @@ -59,8 +67,11 @@ fn build_attributes() -> Retained> { // 段落样式:右对齐,保证定宽视觉一致 let para_style = NSMutableParagraphStyle::new(); para_style.setAlignment(NSTextAlignment::Right); + para_style.setLineBreakMode(NSLineBreakMode::ByClipping); para_style.setLineSpacing(TRAY_LINE_SPACING); para_style.setLineHeightMultiple(TRAY_LINE_HEIGHT_MULTIPLE); + para_style.setMinimumLineHeight(TRAY_LINE_HEIGHT); + para_style.setMaximumLineHeight(TRAY_LINE_HEIGHT); para_style.setParagraphSpacingBefore(TRAY_PARAGRAPH_SPACING_BEFORE); // 基线偏移:用于精确控制两行速率整体的纵向位置 let baseline_offset = NSNumber::new_f64(TRAY_BASELINE_OFFSET); @@ -90,6 +101,10 @@ fn create_attributed_string( } } +fn status_item_length_for_speed(attr_str: &NSAttributedString) -> f64 { + (attr_str.size().width.ceil() + TRAY_STATUS_ITEM_EXTRA_WIDTH).max(TRAY_STATUS_ITEM_MIN_LENGTH) +} + fn sync_click_target_frame(button: &NSStatusBarButton) { let bounds = button.bounds(); let subviews = button.subviews(); @@ -122,6 +137,9 @@ fn apply_status_item_attributed_title( return; }; let attr_str = create_attributed_string(text, attrs); + if attrs.is_some() { + status_item.setLength(status_item_length_for_speed(&attr_str)); + } button.setAttributedTitle(&attr_str); sync_click_target_frame(&button); } @@ -158,6 +176,10 @@ pub fn set_speed_attributed_title(status_item: &NSStatusItem, up: u64, down: u64 /// # Arguments /// * `status_item` - macOS 托盘 NSStatusItem 引用 pub fn clear_speed_attributed_title(status_item: &NSStatusItem) { + LAST_DISPLAY_STR.with(|last| { + last.borrow_mut().clear(); + }); let empty = NSString::from_str(""); + status_item.setLength(NS_VARIABLE_STATUS_ITEM_LENGTH); apply_status_item_attributed_title(status_item, &empty, None); }