window_enumerator/
models.rs

1use crate::types::WindowInfo;
2
3#[cfg(feature = "sorting")]
4use crate::types::{PositionSort, SortCriteria};
5
6#[cfg(feature = "sorting")]
7use crate::utils::matches_criteria;
8
9/// Extension methods for [`WindowInfo`] providing display and validation functionality.
10impl WindowInfo {
11    /// Prints detailed information about the window to stdout.
12    ///
13    /// # Examples
14    /// ```
15    /// # use window_enumerator::WindowInfo;
16    /// # use window_enumerator::WindowPosition;
17    /// # let window = WindowInfo {
18    /// #     hwnd: 12345,
19    /// #     pid: 1234,
20    /// #     title: "Test".to_string(),
21    /// #     class_name: "TestClass".to_string(),
22    /// #     process_name: "test.exe".to_string(),
23    /// #     process_file: std::path::PathBuf::from("test.exe"),
24    /// #     index: 1,
25    /// #     position: WindowPosition::default(),
26    /// # };
27    /// window.print();
28    /// ```
29    pub fn print(&self) {
30        println!("Index: {}", self.index);
31        println!("Window Handle: 0x{:x}", self.hwnd);
32        println!("Process ID: {}", self.pid);
33        println!("Title: {}", self.title);
34        println!("Class Name: {}", self.class_name);
35        println!("Process Name: {}", self.process_name);
36        println!("Process File: {}", self.process_file.display());
37        println!(
38            "Position: ({}, {}) Size: {}x{}",
39            self.position.x, self.position.y, self.position.width, self.position.height
40        );
41        println!("----------------------------------------");
42    }
43
44    /// Prints compact window information to stdout.
45    ///
46    /// # Examples
47    /// ```
48    /// # use window_enumerator::WindowInfo;
49    /// # use window_enumerator::WindowPosition;
50    /// # let window = WindowInfo {
51    /// #     hwnd: 12345,
52    /// #     pid: 1234,
53    /// #     title: "Test".to_string(),
54    /// #     class_name: "TestClass".to_string(),
55    /// #     process_name: "test.exe".to_string(),
56    /// #     process_file: std::path::PathBuf::from("test.exe"),
57    /// #     index: 1,
58    /// #     position: WindowPosition::default(),
59    /// # };
60    /// window.print_compact();
61    /// ```
62    pub fn print_compact(&self) {
63        println!(
64            "[{}] 0x{:x} (PID: {}) @ ({},{}) - {}",
65            self.index, self.hwnd, self.pid, self.position.x, self.position.y, self.title
66        );
67    }
68
69    /// Checks if the window handle is still valid.
70    ///
71    /// This verifies that the window still exists in the system.
72    ///
73    /// # Examples
74    /// ```
75    /// # use window_enumerator::WindowInfo;
76    /// # use window_enumerator::WindowPosition;
77    /// # let window = WindowInfo {
78    /// #     hwnd: 12345,
79    /// #     pid: 1234,
80    /// #     title: "Test".to_string(),
81    /// #     class_name: "TestClass".to_string(),
82    /// #     process_name: "test.exe".to_string(),
83    /// #     process_file: std::path::PathBuf::from("test.exe"),
84    /// #     index: 1,
85    /// #     position: WindowPosition::default(),
86    /// # };
87    /// let is_valid = window.is_valid();
88    /// ```
89    #[cfg(feature = "windows")]
90    pub fn is_valid(&self) -> bool {
91        use windows::Win32::Foundation::*;
92        use windows::Win32::UI::WindowsAndMessaging::*;
93
94        unsafe { IsWindow(HWND(self.hwnd)).as_bool() }
95    }
96}
97
98/// Provides window sorting functionality.
99#[cfg(feature = "sorting")]
100pub struct WindowSorter;
101
102#[cfg(feature = "sorting")]
103impl WindowSorter {
104    /// Sorts a vector of windows according to the specified criteria.
105    ///
106    /// # Arguments
107    ///
108    /// * `windows` - The windows to sort (modified in-place)
109    /// * `sort_criteria` - The criteria to use for sorting
110    pub fn sort_windows(windows: &mut [WindowInfo], sort_criteria: &SortCriteria) {
111        // ← 修改参数类型为切片
112        if sort_criteria.pid == 0 && sort_criteria.title == 0 && sort_criteria.position.is_none() {
113            return; // No sorting criteria
114        }
115
116        windows.sort_by(|a, b| {
117            let mut ordering = std::cmp::Ordering::Equal;
118
119            // PID sorting
120            if sort_criteria.pid != 0 {
121                ordering = a.pid.cmp(&b.pid);
122                if sort_criteria.pid < 0 {
123                    ordering = ordering.reverse();
124                }
125                if ordering != std::cmp::Ordering::Equal {
126                    return ordering;
127                }
128            }
129
130            // Title sorting
131            if sort_criteria.title != 0 {
132                ordering = a.title.to_lowercase().cmp(&b.title.to_lowercase());
133                if sort_criteria.title < 0 {
134                    ordering = ordering.reverse();
135                }
136                if ordering != std::cmp::Ordering::Equal {
137                    return ordering;
138                }
139            }
140
141            // Position sorting
142            if let Some(ref position_sort) = sort_criteria.position {
143                ordering = Self::compare_positions(a, b, position_sort);
144                if ordering != std::cmp::Ordering::Equal {
145                    return ordering;
146                }
147            }
148
149            ordering
150        });
151    }
152
153    /// Compares two windows based on position sorting criteria.
154    fn compare_positions(
155        a: &WindowInfo,
156        b: &WindowInfo,
157        position_sort: &PositionSort,
158    ) -> std::cmp::Ordering {
159        match position_sort {
160            PositionSort::X(order) => {
161                let ordering = a.position.x.cmp(&b.position.x);
162                if *order < 0 {
163                    ordering.reverse()
164                } else {
165                    ordering
166                }
167            }
168            PositionSort::Y(order) => {
169                let ordering = a.position.y.cmp(&b.position.y);
170                if *order < 0 {
171                    ordering.reverse()
172                } else {
173                    ordering
174                }
175            }
176            PositionSort::XY(x_order, y_order) => {
177                // Sort by X first
178                let x_ordering = a.position.x.cmp(&b.position.x);
179                if x_ordering != std::cmp::Ordering::Equal {
180                    return if *x_order < 0 {
181                        x_ordering.reverse()
182                    } else {
183                        x_ordering
184                    };
185                }
186
187                // If X is equal, sort by Y
188                let y_ordering = a.position.y.cmp(&b.position.y);
189                if *y_order < 0 {
190                    y_ordering.reverse()
191                } else {
192                    y_ordering
193                }
194            }
195        }
196    }
197
198    /// Filters and sorts windows according to the specified criteria.
199    ///
200    /// # Arguments
201    ///
202    /// * `windows` - The windows to filter and sort
203    /// * `criteria` - The filter criteria
204    /// * `sort_criteria` - The sort criteria
205    ///
206    /// # Returns
207    ///
208    /// A new vector containing the filtered and sorted windows.
209    pub fn filter_and_sort_windows(
210        windows: &[WindowInfo],
211        criteria: &crate::types::FilterCriteria,
212        sort_criteria: &SortCriteria,
213    ) -> Vec<WindowInfo> {
214        let mut filtered: Vec<WindowInfo> = windows
215            .iter()
216            .filter(|window| matches_criteria(window, criteria))
217            .cloned()
218            .collect();
219
220        Self::sort_windows(&mut filtered, sort_criteria);
221        filtered
222    }
223}