Skip to content

Commit 9cf5ce9

Browse files
committed
WIP IsOnCloseButton
1 parent 84ddf60 commit 9cf5ce9

3 files changed

Lines changed: 103 additions & 19 deletions

File tree

src/tabbookmark.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ bool HandleDoubleClick(const MOUSEHOOKSTRUCT* pmouse) {
140140
return false;
141141
}
142142
// Avoid triggering on close button clicks.
143-
if (IsOnCloseButton(tab, pt)) {
143+
if (IsOnCloseButton(pt)) {
144144
return false;
145145
}
146146
if (tab_count == 1) {
@@ -221,7 +221,7 @@ bool HandleCloseButton(const MOUSEHOOKSTRUCT* pmouse) {
221221
// Get the tab under mouse and check if close button is clicked within it.
222222
// This prevents false positives from other PUSHBUTTON elements like
223223
// New Tab Button.
224-
if (!tab || !IsOnCloseButton(tab, pt)) {
224+
if (!tab || !IsOnCloseButton(pt)) {
225225
return false;
226226
}
227227
if (!IsNeedKeep(tab_count, KeepTabTrigger::kCloseButton)) {

src/uia.cc

Lines changed: 100 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -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

138138
bool 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+
157240
bool IsOnBookmark(POINT pt) {
158241
if (!EnsureAutomation()) {
159242
return false;

src/uia.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include <windows.h>
55

6+
bool IsOnCloseButton(POINT pt);
67
bool IsOnBookmark(POINT pt);
78
bool IsOmniboxFocused();
89

0 commit comments

Comments
 (0)