Skip to content

Commit 9adbe11

Browse files
committed
IsOnTabBar, improvements
1 parent 85fdf2d commit 9adbe11

3 files changed

Lines changed: 89 additions & 37 deletions

File tree

src/tabbookmark.cc

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,6 @@ bool HandleMouseWheel(LPARAM lParam, const MOUSEHOOKSTRUCT* pmouse) {
9393
}
9494

9595
HWND hwnd = GetFocus();
96-
const NodePtr top_container_view = GetTopContainerView(hwnd);
97-
9896
const auto* pwheel = reinterpret_cast<const MOUSEHOOKSTRUCTEX*>(lParam);
9997
const int delta = GET_WHEEL_DELTA_WPARAM(pwheel->mouseData);
10098

@@ -108,13 +106,13 @@ bool HandleMouseWheel(LPARAM lParam, const MOUSEHOOKSTRUCT* pmouse) {
108106
return true;
109107
};
110108

111-
// If the mouse wheel is used to switch tabs when the mouse is on the tab bar.
112-
if (config.IsWheelTab() && IsOnTheTabBar(top_container_view, pmouse->pt)) {
109+
// If it is used to switch tabs when the right button is held.
110+
if (config.IsWheelTabWhenPressRightButton() && IsKeyPressed(VK_RBUTTON)) {
113111
return switch_tabs();
114112
}
115113

116-
// If it is used to switch tabs when the right button is held.
117-
if (config.IsWheelTabWhenPressRightButton() && IsKeyPressed(VK_RBUTTON)) {
114+
// If the mouse wheel is used to switch tabs when the mouse is on the tab bar.
115+
if (config.IsWheelTab() && IsOnTabBar(pmouse->pt)) {
118116
return switch_tabs();
119117
}
120118

@@ -129,7 +127,7 @@ bool HandleDoubleClick(const MOUSEHOOKSTRUCT* pmouse) {
129127

130128
const POINT pt = pmouse->pt;
131129
HWND hwnd = WindowFromPoint(pt);
132-
const auto hit = GetTabHitResult(pt, config.IsKeepLastTab());
130+
const auto hit = FindTabHitResult(pt, config.IsKeepLastTab());
133131
if (!hit || !hit->tab || hit->on_close_button) {
134132
return false;
135133
}
@@ -150,7 +148,7 @@ bool HandleRightClick(const MOUSEHOOKSTRUCT* pmouse) {
150148

151149
const POINT pt = pmouse->pt;
152150
HWND hwnd = WindowFromPoint(pt);
153-
const auto hit = GetTabHitResult(pt, config.IsKeepLastTab());
151+
const auto hit = FindTabHitResult(pt, config.IsKeepLastTab());
154152
if (!hit || !hit->tab) {
155153
return false;
156154
}
@@ -173,7 +171,7 @@ bool HandleMiddleClick(const MOUSEHOOKSTRUCT* pmouse) {
173171

174172
const POINT pt = pmouse->pt;
175173
HWND hwnd = WindowFromPoint(pt);
176-
const auto hit = GetTabHitResult(pt, config.IsKeepLastTab());
174+
const auto hit = FindTabHitResult(pt, config.IsKeepLastTab());
177175
if (!hit || !hit->tab) {
178176
return false;
179177
}
@@ -193,7 +191,7 @@ bool HandleCloseButton(const MOUSEHOOKSTRUCT* pmouse) {
193191

194192
const POINT pt = pmouse->pt;
195193
HWND hwnd = WindowFromPoint(pt);
196-
const auto hit = GetTabHitResult(pt, config.IsKeepLastTab());
194+
const auto hit = FindTabHitResult(pt, config.IsKeepLastTab());
197195
if (!hit || !hit->tab || !hit->on_close_button) {
198196
return false;
199197
}

src/uia.cc

Lines changed: 79 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
#include <uiautomation.h>
44
#include <wrl/client.h>
55

6+
#include <algorithm>
7+
#include <initializer_list>
68
#include <optional>
79
#include <string_view>
810

@@ -64,7 +66,7 @@ UiaThreadState& GetUiaThreadState() {
6466
return state;
6567
}
6668

67-
struct TabContext {
69+
struct TabContainer {
6870
ComPtr<IUIAutomationElement> container;
6971
std::wstring_view tab_class;
7072
};
@@ -145,7 +147,7 @@ std::optional<std::wstring> GetCurrentStringProperty(
145147
return std::wstring(property.Ref().bstrVal);
146148
}
147149

148-
ComPtr<IUIAutomationCondition> MakeClassCondition(
150+
ComPtr<IUIAutomationCondition> CreateClassCondition(
149151
const ComPtr<IUIAutomation>& automation,
150152
std::wstring_view class_name) {
151153
if (!automation || class_name.empty()) {
@@ -170,20 +172,24 @@ ComPtr<IUIAutomationCondition> MakeClassCondition(
170172
}
171173

172174
bool IsClassName(const ComPtr<IUIAutomationElement>& element,
173-
std::wstring_view class_name_target) {
174-
if (!element) {
175+
std::wstring_view expected_class_name) {
176+
const auto actual_class_name =
177+
GetCurrentStringProperty(element, UIA_ClassNamePropertyId);
178+
if (!actual_class_name.has_value()) {
175179
return false;
176180
}
181+
return actual_class_name.value() == expected_class_name;
182+
}
177183

178-
BSTR class_name = nullptr;
179-
if (FAILED(element->get_CurrentClassName(&class_name)) || !class_name) {
184+
bool IsAnyClassName(
185+
const ComPtr<IUIAutomationElement>& element,
186+
std::initializer_list<std::wstring_view> expected_class_names) {
187+
const auto actual_class_name =
188+
GetCurrentStringProperty(element, UIA_ClassNamePropertyId);
189+
if (!actual_class_name.has_value()) {
180190
return false;
181191
}
182-
183-
const std::wstring_view class_name_view(class_name);
184-
const bool is_match = (class_name_view == class_name_target);
185-
SysFreeString(class_name);
186-
return is_match;
192+
return std::ranges::contains(expected_class_names, actual_class_name.value());
187193
}
188194

189195
ComPtr<IUIAutomationElement> FindFirstInSubtree(
@@ -236,7 +242,35 @@ ComPtr<IUIAutomationElement> FindSiblingByClass(
236242
return nullptr;
237243
}
238244

239-
std::optional<TabContext> ResolveTabContext(
245+
ComPtr<IUIAutomationElement> FindParentByClass(
246+
const ComPtr<IUIAutomation>& automation,
247+
const ComPtr<IUIAutomationElement>& element,
248+
std::wstring_view class_name) {
249+
if (!automation || !element) {
250+
return nullptr;
251+
}
252+
253+
ComPtr<IUIAutomationTreeWalker> walker;
254+
if (FAILED(automation->get_ControlViewWalker(&walker)) || !walker) {
255+
return nullptr;
256+
}
257+
258+
ComPtr<IUIAutomationElement> current = element;
259+
while (true) {
260+
ComPtr<IUIAutomationElement> parent;
261+
if (FAILED(walker->GetParentElement(current.Get(),
262+
parent.ReleaseAndGetAddressOf())) ||
263+
!parent) {
264+
return nullptr;
265+
}
266+
if (IsClassName(parent, class_name)) {
267+
return parent;
268+
}
269+
current = std::move(parent);
270+
}
271+
}
272+
273+
std::optional<TabContainer> FindTabContainer(
240274
const ComPtr<IUIAutomation>& automation,
241275
POINT pt) {
242276
auto pointed = GetElementFromPoint(automation, pt);
@@ -248,11 +282,11 @@ std::optional<TabContext> ResolveTabContext(
248282
if (IsClassName(pointed, L"ScrollView")) {
249283
auto container = FindFirstInSubtree(
250284
pointed,
251-
MakeClassCondition(automation, L"VerticalUnpinnedTabContainerView"));
285+
CreateClassCondition(automation, L"VerticalUnpinnedTabContainerView"));
252286
if (!container) {
253287
return std::nullopt;
254288
}
255-
return TabContext{container, L"VerticalTabView"};
289+
return TabContainer{container, L"VerticalTabView"};
256290
}
257291

258292
// horizontal tabs
@@ -262,28 +296,28 @@ std::optional<TabContext> ResolveTabContext(
262296
if (!container) {
263297
return std::nullopt;
264298
}
265-
return TabContext{container, L"Tab"};
299+
return TabContainer{container, L"Tab"};
266300
}
267301

268302
return std::nullopt;
269303
}
270304

271305
} // namespace
272306

273-
std::optional<TabHitResult> GetTabHitResult(POINT pt, bool need_count) {
307+
std::optional<TabHitResult> FindTabHitResult(POINT pt, bool need_count) {
274308
if (!EnsureAutomation()) {
275309
return std::nullopt;
276310
}
277311
const auto& automation = GetUiaThreadState().automation;
278312

279-
auto ctx = ResolveTabContext(automation, pt);
280-
if (!ctx) {
313+
auto tab = FindTabContainer(automation, pt);
314+
if (!tab) {
281315
return std::nullopt;
282316
}
283317

284-
auto condition = MakeClassCondition(automation, ctx->tab_class);
318+
auto condition = CreateClassCondition(automation, tab->tab_class);
285319
ComPtr<IUIAutomationElementArray> arr;
286-
if (FAILED(ctx->container->FindAll(TreeScope_Children, condition.Get(),
320+
if (FAILED(tab->container->FindAll(TreeScope_Children, condition.Get(),
287321
arr.ReleaseAndGetAddressOf())) ||
288322
!arr) {
289323
return std::nullopt;
@@ -312,7 +346,7 @@ std::optional<TabHitResult> GetTabHitResult(POINT pt, bool need_count) {
312346

313347
result.tab = tab;
314348
auto close_btn = FindFirstInSubtree(
315-
tab, MakeClassCondition(automation, L"TabCloseButton"));
349+
tab, CreateClassCondition(automation, L"TabCloseButton"));
316350
if (close_btn) {
317351
RECT close_rect;
318352
if (SUCCEEDED(close_btn->get_CurrentBoundingRectangle(&close_rect))) {
@@ -325,6 +359,28 @@ std::optional<TabHitResult> GetTabHitResult(POINT pt, bool need_count) {
325359
return result;
326360
}
327361

362+
bool IsOnTabBar(POINT pt) {
363+
if (!EnsureAutomation()) {
364+
return false;
365+
}
366+
const auto& automation = GetUiaThreadState().automation;
367+
368+
const auto pointed = GetElementFromPoint(automation, pt);
369+
if (!pointed) {
370+
return false;
371+
}
372+
373+
// Fast path to avoid `GetParentElement` calls for common tab bar elements.
374+
if (IsAnyClassName(
375+
pointed,
376+
{L"HorizontalTabStripRegionView", L"TabStrip::TabDragContextImpl",
377+
L"TabStripControlButton", L"FrameGrabHandle", L"ScrollView"})) {
378+
return true;
379+
}
380+
return FindParentByClass(automation, pointed,
381+
L"HorizontalTabStripRegionView") != nullptr;
382+
}
383+
328384
bool IsOnBookmark(POINT pt) {
329385
if (!EnsureAutomation()) {
330386
return false;
@@ -350,9 +406,7 @@ bool IsOnBookmark(POINT pt) {
350406
if (is_blocked_scheme || !looks_like_url) {
351407
return false;
352408
}
353-
354-
return IsClassName(pointed, L"BookmarkButton") ||
355-
IsClassName(pointed, L"MenuItemView");
409+
return IsAnyClassName(pointed, {L"BookmarkButton", L"MenuItemView"});
356410
}
357411

358412
bool IsOmniboxFocused() {
@@ -365,6 +419,5 @@ bool IsOmniboxFocused() {
365419
if (!focused) {
366420
return false;
367421
}
368-
return IsClassName(focused, L"OmniboxViewViews") ||
369-
IsClassName(focused, L"OmniboxResultView");
422+
return IsAnyClassName(focused, {L"OmniboxViewViews", L"OmniboxResultView"});
370423
}

src/uia.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ struct TabHitResult {
1212
bool on_close_button = false;
1313
};
1414

15-
std::optional<TabHitResult> GetTabHitResult(POINT pt, bool need_count);
15+
std::optional<TabHitResult> FindTabHitResult(POINT pt, bool need_count);
16+
bool IsOnTabBar(POINT pt);
1617
bool IsOnBookmark(POINT pt);
1718
bool IsOmniboxFocused();
1819

0 commit comments

Comments
 (0)