From 494f368b8dd3b44cb98953d4446b727ee73b75f0 Mon Sep 17 00:00:00 2001 From: abab5601 Date: Wed, 12 Aug 2026 16:24:54 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=BE=A9=E5=A4=9A=E8=9E=A2?= =?UTF-8?q?=E5=B9=95=E5=B7=A5=E4=BD=9C=E5=88=97=E5=AE=9A=E4=BD=8D=E3=80=81?= =?UTF-8?q?=E5=89=AF=E8=9E=A2=E5=B9=95=E6=99=82=E9=90=98=E5=8D=80=E8=BE=A8?= =?UTF-8?q?=E8=AD=98=E8=88=87=E8=B7=A8=E8=9E=A2=E5=B9=95=E6=B5=81=E6=9A=A2?= =?UTF-8?q?=E6=8B=96=E6=9B=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/native_interop.rs | 55 ++++++++++++++++++++ src/window.rs | 117 ++++++++++++++++++++++++++++++++---------- 2 files changed, 145 insertions(+), 27 deletions(-) diff --git a/src/native_interop.rs b/src/native_interop.rs index c745d08..20e24b8 100644 --- a/src/native_interop.rs +++ b/src/native_interop.rs @@ -77,6 +77,61 @@ pub fn find_child_window(parent: HWND, class_name: &str) -> Option { } } +/// Find any tray or clock child window in a taskbar (for primary or secondary taskbars) +pub fn find_taskbar_tray_or_clock_window(taskbar_hwnd: HWND) -> Option { + const CANDIDATES: &[&str] = &[ + "TrayNotifyWnd", + "Taskbar.SecondaryTrayHostControl", + "TrayClockWClass", + "ClockButton", + ]; + for &class_name in CANDIDATES { + if let Some(h) = find_child_window(taskbar_hwnd, class_name) { + return Some(h); + } + } + None +} + +/// Find the leftmost X coordinate of right-side child controls inside a taskbar +pub fn find_taskbar_right_controls_left(taskbar_hwnd: HWND, taskbar_rect: RECT) -> Option { + if let Some(tray_hwnd) = find_taskbar_tray_or_clock_window(taskbar_hwnd) { + if let Some(tray_rect) = get_window_rect_safe(tray_hwnd) { + return Some(tray_rect.left); + } + } + + unsafe extern "system" fn enum_child_proc(child_hwnd: HWND, lparam: LPARAM) -> BOOL { + let (taskbar_rect, min_left) = &mut *(lparam.0 as *mut (RECT, Option)); + if let Some(rect) = get_window_rect_safe(child_hwnd) { + let taskbar_width = taskbar_rect.right - taskbar_rect.left; + // Only search in the rightmost 25% of the taskbar to avoid taskbar app buttons + let right_zone_x = taskbar_rect.right - (taskbar_width / 4); + if rect.left >= right_zone_x && rect.left < taskbar_rect.right && (rect.right - rect.left) > 0 { + match min_left { + Some(current) => { + if rect.left < *current { + *min_left = Some(rect.left); + } + } + None => *min_left = Some(rect.left), + } + } + } + BOOL(1) + } + + let mut data = (taskbar_rect, None); + unsafe { + let _ = EnumChildWindows( + taskbar_hwnd, + Some(enum_child_proc), + LPARAM(&mut data as *mut _ as isize), + ); + } + data.1 +} + /// Get taskbar position via SHAppBarMessage pub fn get_taskbar_rect(taskbar_hwnd: HWND) -> Option { unsafe { diff --git a/src/window.rs b/src/window.rs index bf30f88..da4431d 100644 --- a/src/window.rs +++ b/src/window.rs @@ -132,6 +132,7 @@ const IDM_LANG_SIMPLIFIED_CHINESE: u16 = 51; const IDM_MODEL_CLAUDE_CODE: u16 = 60; const IDM_MODEL_CODEX: u16 = 61; const IDM_MODEL_ANTIGRAVITY: u16 = 62; +const IDM_TASKBAR_BASE: u16 = 70; const WM_DPICHANGED_MSG: u32 = 0x02E0; const WM_APP_UPDATE_CHECK_COMPLETE: u32 = WM_APP + 2; @@ -524,11 +525,11 @@ fn attach_to_taskbar(hwnd: HWND, requested_index: usize) -> bool { native_interop::embed_in_taskbar(hwnd, taskbar.hwnd); - let tray_notify = native_interop::find_child_window(taskbar.hwnd, "TrayNotifyWnd"); + let tray_notify = native_interop::find_taskbar_tray_or_clock_window(taskbar.hwnd); if tray_notify.is_some() { - diagnose::log("TrayNotifyWnd found"); + diagnose::log("tray/clock control found"); } else { - diagnose::log("TrayNotifyWnd not found"); + diagnose::log("tray/clock control not found"); } let hook = tray_notify.and_then(|tray_hwnd| { @@ -565,13 +566,8 @@ fn taskbar_at_point(pt: POINT) -> Option<(usize, native_interop::TaskbarWindow)> } fn tray_left_for_taskbar(taskbar_hwnd: HWND, taskbar_rect: RECT) -> i32 { - let mut tray_left = taskbar_rect.right; - if let Some(tray_hwnd) = native_interop::find_child_window(taskbar_hwnd, "TrayNotifyWnd") { - if let Some(tray_rect) = native_interop::get_window_rect_safe(tray_hwnd) { - tray_left = tray_rect.left; - } - } - tray_left + native_interop::find_taskbar_right_controls_left(taskbar_hwnd, taskbar_rect) + .unwrap_or(taskbar_rect.right) } fn clamp_offset_for_taskbar(taskbar_hwnd: HWND, taskbar_rect: RECT, offset: i32) -> i32 { @@ -2097,16 +2093,10 @@ fn position_at_taskbar() { }; let taskbar_height = taskbar_rect.bottom - taskbar_rect.top; - let mut tray_left = taskbar_rect.right; + let tray_left = tray_left_for_taskbar(taskbar_hwnd, taskbar_rect); let anchor_top = taskbar_rect.top; let anchor_height = taskbar_height; - if let Some(tray_hwnd) = native_interop::find_child_window(taskbar_hwnd, "TrayNotifyWnd") { - if let Some(tray_rect) = native_interop::get_window_rect_safe(tray_hwnd) { - tray_left = tray_rect.left; - } - } - let widget_width = total_widget_width(); let max_offset = (tray_left - taskbar_rect.left - widget_width).max(0); let tray_offset = tray_offset.clamp(0, max_offset); @@ -2368,6 +2358,38 @@ unsafe extern "system" fn wnd_proc( if is_dragging { let mut pt = POINT::default(); let _ = GetCursorPos(&mut pt); + + let target_switch = { + let state = lock_state(); + state.as_ref().and_then(|s| { + if let Some((target_index, _)) = taskbar_at_point(pt) { + if target_index != s.taskbar_index { + Some(target_index) + } else { + None + } + } else { + None + } + }) + }; + + if let Some(target_index) = target_switch { + let mut state = lock_state(); + if let Some(s) = state.as_mut() { + s.taskbar_index = target_index; + s.drag_start_mouse_x = pt.x; + s.drag_start_offset = 0; + s.tray_offset = 0; + } + drop(state); + if attach_to_taskbar(hwnd, target_index) { + position_at_taskbar(); + render_layered(); + } + return LRESULT(0); + } + let move_target = { let mut state = lock_state(); let s = match state.as_mut() { @@ -2391,16 +2413,7 @@ unsafe extern "system" fn wnd_proc( // Clamp: don't go past left edge of taskbar if let Some(taskbar_hwnd) = taskbar_hwnd { if let Some(taskbar_rect) = native_interop::get_taskbar_rect(taskbar_hwnd) { - let mut tray_left = taskbar_rect.right; - if let Some(tray_hwnd) = - native_interop::find_child_window(taskbar_hwnd, "TrayNotifyWnd") - { - if let Some(tray_rect) = - native_interop::get_window_rect_safe(tray_hwnd) - { - tray_left = tray_rect.left; - } - } + let tray_left = tray_left_for_taskbar(taskbar_hwnd, taskbar_rect); let widget_width = total_widget_width_for_state(s); let max_offset = (tray_left - taskbar_rect.left - widget_width).max(0); if new_offset > max_offset { @@ -2577,6 +2590,21 @@ unsafe extern "system" fn wnd_proc( IDM_START_WITH_WINDOWS => { set_startup_enabled(!is_startup_enabled()); } + id if id >= IDM_TASKBAR_BASE && id < IDM_TASKBAR_BASE + 10 => { + let target_index = (id - IDM_TASKBAR_BASE) as usize; + { + let mut state = lock_state(); + if let Some(s) = state.as_mut() { + s.taskbar_index = target_index; + s.tray_offset = 0; + } + } + if attach_to_taskbar(hwnd, target_index) { + position_at_taskbar(); + render_layered(); + } + save_state_settings(); + } IDM_FREQ_1MIN | IDM_FREQ_5MIN | IDM_FREQ_15MIN | IDM_FREQ_1HOUR => { let new_interval = match id { IDM_FREQ_1MIN => POLL_1_MIN, @@ -2862,6 +2890,41 @@ fn show_context_menu(hwnd: HWND) { PCWSTR::from_raw(reset_pos_str.as_ptr()), ); + let taskbars = native_interop::find_taskbars(); + if taskbars.len() > 1 { + let screen_menu = CreatePopupMenu().unwrap(); + let current_index = { + let state = lock_state(); + state.as_ref().map(|s| s.taskbar_index).unwrap_or(0) + }; + for (idx, _) in taskbars.iter().enumerate() { + let label = if idx == 0 { + "Screen 1 (Primary)".to_string() + } else { + format!("Screen {}", idx + 1) + }; + let label_str = native_interop::wide_str(&label); + let flags = if idx == current_index { + MF_CHECKED + } else { + MENU_ITEM_FLAGS(0) + }; + let _ = AppendMenuW( + screen_menu, + flags, + (IDM_TASKBAR_BASE + idx as u16) as usize, + PCWSTR::from_raw(label_str.as_ptr()), + ); + } + let screen_label = native_interop::wide_str("Screen / Taskbar"); + let _ = AppendMenuW( + settings_menu, + MF_POPUP, + screen_menu.0 as usize, + PCWSTR::from_raw(screen_label.as_ptr()), + ); + } + let language_menu = CreatePopupMenu().unwrap(); let system_label = native_interop::wide_str(strings.system_default); let system_flags = if language_override.is_none() {