@@ -113,26 +113,26 @@ bool TryGetCurrentStringProperty(const ComPtr<IUIAutomationElement>& element,
113113 return true ;
114114}
115115
116- bool FindFirstInSubtree (
117- const ComPtr<IUIAutomationElement>& root,
118- const ComPtr<IUIAutomationCondition>& condition,
119- const ComPtr<IUIAutomationCacheRequest>& cache_request) {
120- if (!root || !condition) {
121- return false ;
122- }
123-
124- ComPtr<IUIAutomationElement> hit;
125- if (SUCCEEDED (root->FindFirstBuildCache (TreeScope_Subtree, condition.Get (),
126- cache_request.Get (), &hit)) &&
127- hit != nullptr ) {
128- return true ;
116+ ComPtr<IUIAutomationCondition> MakeClassCondition (
117+ const ComPtr<IUIAutomation>& automation,
118+ std::wstring_view class_name) {
119+ ScopedVariant val;
120+ val.value .vt = VT_BSTR;
121+ val.value .bstrVal = SysAllocStringLen (class_name.data (),
122+ static_cast <UINT>(class_name.size ()));
123+ if (!val.value .bstrVal ) {
124+ return nullptr ;
129125 }
130126
131- hit.Reset ();
132- if (FAILED (root->FindFirst (TreeScope_Subtree, condition.Get (), &hit))) {
133- return false ;
127+ ComPtr<IUIAutomationCondition> condition;
128+ if (FAILED (automation->CreatePropertyCondition (
129+ UIA_ClassNamePropertyId, val.value ,
130+ condition.ReleaseAndGetAddressOf ()))) {
131+ {
132+ return nullptr ;
133+ }
134134 }
135- return hit != nullptr ;
135+ return condition ;
136136}
137137
138138bool IsClassName (const ComPtr<IUIAutomationElement>& element,
@@ -152,8 +152,91 @@ bool IsClassName(const ComPtr<IUIAutomationElement>& element,
152152 return is_match;
153153}
154154
155+ ComPtr<IUIAutomationElement> FindFirstInSubtree (
156+ const ComPtr<IUIAutomationElement>& root,
157+ const ComPtr<IUIAutomationCondition>& condition) {
158+ if (!root || !condition) {
159+ return nullptr ;
160+ }
161+
162+ // TODO: consider caching
163+ ComPtr<IUIAutomationElement> hit;
164+ if (FAILED (root->FindFirst (TreeScope_Subtree, condition.Get (),
165+ hit.ReleaseAndGetAddressOf ()))) {
166+ return nullptr ;
167+ }
168+ return hit;
169+ }
170+
171+ bool FindSiblingByClass (const ComPtr<IUIAutomation>& automation,
172+ const ComPtr<IUIAutomationElement>& element,
173+ std::wstring_view class_name,
174+ ComPtr<IUIAutomationElement>* out) {
175+ ComPtr<IUIAutomationTreeWalker> walker;
176+ if (FAILED (automation->get_ControlViewWalker (&walker)) || !walker) {
177+ return false ;
178+ }
179+
180+ for (bool forward : {true , false }) {
181+ ComPtr<IUIAutomationElement> current;
182+ HRESULT hr = forward ? walker->GetNextSiblingElement (
183+ element.Get (), current.ReleaseAndGetAddressOf ())
184+ : walker->GetPreviousSiblingElement (
185+ element.Get (), current.ReleaseAndGetAddressOf ());
186+ while (SUCCEEDED (hr) && current) {
187+ if (IsClassName (current, class_name)) {
188+ *out = std::move (current);
189+ return true ;
190+ }
191+ ComPtr<IUIAutomationElement> next;
192+ hr = forward ? walker->GetNextSiblingElement (
193+ current.Get (), next.ReleaseAndGetAddressOf ())
194+ : walker->GetPreviousSiblingElement (
195+ current.Get (), next.ReleaseAndGetAddressOf ());
196+ current = std::move (next);
197+ }
198+ }
199+ return false ;
200+ }
201+
155202} // namespace
156203
204+ bool IsOnCloseButton (POINT pt) {
205+ if (!EnsureAutomation ()) {
206+ return false ;
207+ }
208+ const auto & automation = GetUiaThreadState ().automation ;
209+
210+ ComPtr<IUIAutomationElement> pointed;
211+ if (!TryGetElementFromPoint (automation, pt, &pointed)) {
212+ return false ;
213+ }
214+
215+ ComPtr<IUIAutomationElement> root;
216+ if (IsClassName (pointed, L" ScrollView" )) {
217+ // vertical tabs
218+ root = pointed;
219+ } else if (IsClassName (pointed, L" TabStrip::TabDragContextImpl" )) {
220+ // horizontal tabs
221+ if (!FindSiblingByClass (automation, pointed, L" TabContainerImpl" , &root)) {
222+ return false ;
223+ }
224+ } else {
225+ return false ;
226+ }
227+
228+ auto close_btn = FindFirstInSubtree (
229+ root, MakeClassCondition (automation, L" TabCloseButton" ));
230+ if (!close_btn) {
231+ return false ;
232+ }
233+
234+ RECT rect;
235+ if (FAILED (close_btn->get_CurrentBoundingRectangle (&rect)))
236+ return false ;
237+ return PtInRect (&rect, pt) != FALSE ;
238+ }
239+
157240bool IsOnBookmark (POINT pt) {
158241 if (!EnsureAutomation ()) {
159242 return false ;
0 commit comments