|
7 | 7 | #include <initializer_list> |
8 | 8 | #include <optional> |
9 | 9 | #include <string_view> |
| 10 | +#include <vector> |
10 | 11 |
|
11 | 12 | #include "utils.h" |
12 | 13 |
|
@@ -208,6 +209,103 @@ ComPtr<IUIAutomationElement> FindFirstInSubtree( |
208 | 209 | return hit; |
209 | 210 | } |
210 | 211 |
|
| 212 | +ComPtr<IUIAutomationElement> FindFirstInSubtreeRaw( |
| 213 | + const ComPtr<IUIAutomation>& automation, |
| 214 | + const ComPtr<IUIAutomationElement>& root, |
| 215 | + std::wstring_view class_name) { |
| 216 | + ComPtr<IUIAutomationTreeWalker> walker; |
| 217 | + if (FAILED(automation->get_RawViewWalker(&walker)) || !walker) { |
| 218 | + return nullptr; |
| 219 | + } |
| 220 | + |
| 221 | + std::vector<ComPtr<IUIAutomationElement>> stack = {root}; |
| 222 | + while (!stack.empty()) { |
| 223 | + auto node = std::move(stack.back()); |
| 224 | + stack.pop_back(); |
| 225 | + if (IsClassName(node, class_name)) { |
| 226 | + return node; |
| 227 | + } |
| 228 | + ComPtr<IUIAutomationElement> child; |
| 229 | + if (FAILED(walker->GetFirstChildElement(node.Get(), |
| 230 | + child.ReleaseAndGetAddressOf()))) { |
| 231 | + continue; |
| 232 | + } |
| 233 | + while (child) { |
| 234 | + stack.push_back(child); |
| 235 | + ComPtr<IUIAutomationElement> next; |
| 236 | + if (FAILED(walker->GetNextSiblingElement( |
| 237 | + child.Get(), next.ReleaseAndGetAddressOf()))) { |
| 238 | + break; |
| 239 | + } |
| 240 | + child = std::move(next); |
| 241 | + } |
| 242 | + } |
| 243 | + return nullptr; |
| 244 | +} |
| 245 | + |
| 246 | +std::optional<int> CountClassInSubtreeRaw( |
| 247 | + const ComPtr<IUIAutomation>& automation, |
| 248 | + const ComPtr<IUIAutomationElement>& root, |
| 249 | + std::wstring_view class_name) { |
| 250 | + ComPtr<IUIAutomationTreeWalker> walker; |
| 251 | + if (FAILED(automation->get_RawViewWalker(&walker)) || !walker) { |
| 252 | + return std::nullopt; |
| 253 | + } |
| 254 | + |
| 255 | + int count = 0; |
| 256 | + std::vector<ComPtr<IUIAutomationElement>> stack = {root}; |
| 257 | + while (!stack.empty()) { |
| 258 | + auto node = std::move(stack.back()); |
| 259 | + stack.pop_back(); |
| 260 | + if (IsClassName(node, class_name)) { |
| 261 | + ++count; |
| 262 | + } |
| 263 | + ComPtr<IUIAutomationElement> child; |
| 264 | + if (FAILED(walker->GetFirstChildElement(node.Get(), |
| 265 | + child.ReleaseAndGetAddressOf()))) { |
| 266 | + continue; |
| 267 | + } |
| 268 | + while (child) { |
| 269 | + stack.push_back(child); |
| 270 | + ComPtr<IUIAutomationElement> next; |
| 271 | + if (FAILED(walker->GetNextSiblingElement( |
| 272 | + child.Get(), next.ReleaseAndGetAddressOf()))) { |
| 273 | + break; |
| 274 | + } |
| 275 | + child = std::move(next); |
| 276 | + } |
| 277 | + } |
| 278 | + return count; |
| 279 | +} |
| 280 | + |
| 281 | +std::optional<TabContainer> FindTabContainerFromHwnd( |
| 282 | + const ComPtr<IUIAutomation>& automation, |
| 283 | + HWND hwnd) { |
| 284 | + auto root = GetElementFromHandle(automation, hwnd); |
| 285 | + if (!root) { |
| 286 | + return std::nullopt; |
| 287 | + } |
| 288 | + |
| 289 | + if (auto c = FindFirstInSubtree( |
| 290 | + root, CreateClassCondition(automation, L"TabContainerImpl"))) |
| 291 | + return TabContainer{c, L"Tab"}; |
| 292 | + if (auto c = FindFirstInSubtree( |
| 293 | + root, CreateClassCondition(automation, |
| 294 | + L"VerticalUnpinnedTabContainerView"))) { |
| 295 | + return TabContainer{c, L"VerticalTabView"}; |
| 296 | + } |
| 297 | + |
| 298 | + if (auto c = FindFirstInSubtreeRaw(automation, root, L"TabContainerImpl")) { |
| 299 | + return TabContainer{c, L"Tab"}; |
| 300 | + } |
| 301 | + if (auto c = FindFirstInSubtreeRaw(automation, root, |
| 302 | + L"VerticalUnpinnedTabContainerView")) { |
| 303 | + return TabContainer{c, L"VerticalTabView"}; |
| 304 | + } |
| 305 | + |
| 306 | + return std::nullopt; |
| 307 | +} |
| 308 | + |
211 | 309 | ComPtr<IUIAutomationElement> FindSiblingByClass( |
212 | 310 | const ComPtr<IUIAutomation>& automation, |
213 | 311 | const ComPtr<IUIAutomationElement>& element, |
@@ -400,6 +498,37 @@ std::optional<TabHitResult> FindTabHitResult(POINT pt, |
400 | 498 | return std::nullopt; |
401 | 499 | } |
402 | 500 |
|
| 501 | +std::optional<int> FindTabCount(HWND hwnd) { |
| 502 | + if (!EnsureAutomation()) { |
| 503 | + return std::nullopt; |
| 504 | + } |
| 505 | + const auto& automation = GetUiaThreadState().automation; |
| 506 | + |
| 507 | + const auto tab_container = FindTabContainerFromHwnd(automation, hwnd); |
| 508 | + if (!tab_container) { |
| 509 | + return std::nullopt; |
| 510 | + } |
| 511 | + |
| 512 | + const auto condition = |
| 513 | + CreateClassCondition(automation, tab_container->tab_class); |
| 514 | + if (!condition) { |
| 515 | + return std::nullopt; |
| 516 | + } |
| 517 | + |
| 518 | + ComPtr<IUIAutomationElementArray> arr; |
| 519 | + if (SUCCEEDED(tab_container->container->FindAll( |
| 520 | + TreeScope_Children, condition.Get(), arr.ReleaseAndGetAddressOf())) && |
| 521 | + arr) { |
| 522 | + int count = 0; |
| 523 | + if (SUCCEEDED(arr->get_Length(&count)) && count > 0) { |
| 524 | + return count; |
| 525 | + } |
| 526 | + } |
| 527 | + // Fullscreen |
| 528 | + return CountClassInSubtreeRaw(automation, tab_container->container, |
| 529 | + tab_container->tab_class); |
| 530 | +} |
| 531 | + |
403 | 532 | bool IsOnTabBar(POINT pt) { |
404 | 533 | if (!EnsureAutomation()) { |
405 | 534 | return false; |
|
0 commit comments