feat(focus): 完成阶段 2 通用焦点系统
This commit is contained in:
@@ -25,6 +25,38 @@ namespace
|
||||
bool model() const override { return false; }
|
||||
};
|
||||
|
||||
class FocusTestControl final : public Control
|
||||
{
|
||||
bool focusEnabled = true;
|
||||
std::function<void(bool, StellarX::FocusReason)> focusCallback;
|
||||
public:
|
||||
explicit FocusTestControl(int tabOrder = 0) : Control(0, 0, 10, 10)
|
||||
{
|
||||
id = "FocusTest";
|
||||
focusPolicy = StellarX::FocusPolicy::Programmatic |
|
||||
StellarX::FocusPolicy::Pointer | StellarX::FocusPolicy::Tab;
|
||||
tabIndex = tabOrder;
|
||||
}
|
||||
void draw() override { dirty = false; }
|
||||
bool handleEvent(const ExMessage&) override { return false; }
|
||||
bool model() const override { return false; }
|
||||
void setFocusEnabledForTest(bool value)
|
||||
{
|
||||
focusEnabled = value;
|
||||
notifyFocusSubtreeStateChanged();
|
||||
}
|
||||
void setFocusCallback(std::function<void(bool, StellarX::FocusReason)> callback)
|
||||
{
|
||||
focusCallback = std::move(callback);
|
||||
}
|
||||
protected:
|
||||
bool isFocusEnabled() const override { return focusEnabled; }
|
||||
void onLogicalFocusChanged(bool focused, StellarX::FocusReason reason) override
|
||||
{
|
||||
if (focusCallback) focusCallback(focused, reason);
|
||||
}
|
||||
};
|
||||
|
||||
std::filesystem::path MakeTempLogPath(const char* name)
|
||||
{
|
||||
// 使用进程号隔离并行测试产生的日志路径;测试结束后由用例主动清理。
|
||||
@@ -169,6 +201,352 @@ namespace StellarXNativeTests
|
||||
}
|
||||
};
|
||||
|
||||
TEST_CLASS(FocusManagerTests)
|
||||
{
|
||||
public:
|
||||
TEST_METHOD(NestedControlResolvesByFocusTargetId)
|
||||
{
|
||||
Window window(200, 120, 1);
|
||||
auto canvas = std::make_unique<Canvas>(0, 0, 100, 100);
|
||||
auto child = std::make_unique<FocusTestControl>();
|
||||
FocusTestControl* childRaw = child.get();
|
||||
const auto childHandle = canvas->addControl(std::move(child));
|
||||
window.addControl(std::move(canvas));
|
||||
|
||||
const auto result = window.getFocusManager().requestFocus({ childHandle, 0 });
|
||||
Assert::AreEqual(static_cast<int>(StellarX::FocusError::None),
|
||||
static_cast<int>(result.error));
|
||||
Assert::IsTrue(childRaw->hasLogicalFocus());
|
||||
Assert::AreEqual(childHandle.value,
|
||||
window.getFocusManager().focusedTarget().owner.value);
|
||||
|
||||
const auto stops = window.getFocusManager().collectTabStops();
|
||||
Assert::AreEqual<size_t>(1, stops.size());
|
||||
Assert::AreEqual(childHandle.value, stops.front().id.owner.value);
|
||||
Assert::AreEqual<std::uint32_t>(0, stops.front().id.subNode);
|
||||
}
|
||||
|
||||
TEST_METHOD(PointerDownMovesLogicalFocusWithoutKeyboardRouting)
|
||||
{
|
||||
Window window(240, 120, 1);
|
||||
auto button = std::make_unique<Button>(10, 10, 80, 30, "button");
|
||||
Button* buttonRaw = button.get();
|
||||
window.addControl(std::move(button));
|
||||
auto text = std::make_unique<TextBox>(110, 10, 100, 30, "text");
|
||||
TextBox* textRaw = text.get();
|
||||
window.addControl(std::move(text));
|
||||
|
||||
ExMessage down{}; down.message = WM_LBUTTONDOWN; down.x = 20; down.y = 20;
|
||||
Assert::IsTrue(buttonRaw->handleEvent(down));
|
||||
Assert::IsTrue(buttonRaw->hasLogicalFocus());
|
||||
|
||||
down.x = 120;
|
||||
Assert::IsTrue(textRaw->handleEvent(down));
|
||||
Assert::IsFalse(buttonRaw->hasLogicalFocus());
|
||||
Assert::IsTrue(textRaw->hasLogicalFocus());
|
||||
}
|
||||
|
||||
TEST_METHOD(FocusLostCallbackCanDeleteNewTarget)
|
||||
{
|
||||
Window window(200, 120, 1);
|
||||
auto oldControl = std::make_unique<FocusTestControl>();
|
||||
FocusTestControl* oldRaw = oldControl.get();
|
||||
const auto oldHandle = window.addControl(std::move(oldControl));
|
||||
auto newControl = std::make_unique<FocusTestControl>();
|
||||
const auto newHandle = window.addControl(std::move(newControl));
|
||||
|
||||
window.getFocusManager().requestFocus({ oldHandle, 0 });
|
||||
oldRaw->setFocusCallback([&](bool focused, StellarX::FocusReason)
|
||||
{
|
||||
if (!focused) Assert::IsTrue(window.removeControl(newHandle));
|
||||
});
|
||||
const auto result = window.getFocusManager().requestFocus({ newHandle, 0 });
|
||||
|
||||
Assert::AreEqual(static_cast<int>(StellarX::FocusError::InvalidTarget),
|
||||
static_cast<int>(result.error));
|
||||
Assert::IsFalse(static_cast<bool>(window.getFocusManager().focusedTarget()));
|
||||
Assert::IsNull(window.findControl(newHandle));
|
||||
Assert::IsFalse(oldRaw->hasLogicalFocus());
|
||||
}
|
||||
|
||||
TEST_METHOD(FocusLostCallbackForcesFreshEnabledSnapshot)
|
||||
{
|
||||
Window window(200, 120, 1);
|
||||
auto oldControl = std::make_unique<FocusTestControl>();
|
||||
FocusTestControl* oldRaw = oldControl.get();
|
||||
const auto oldHandle = window.addControl(std::move(oldControl));
|
||||
auto newControl = std::make_unique<FocusTestControl>();
|
||||
FocusTestControl* newRaw = newControl.get();
|
||||
const auto newHandle = window.addControl(std::move(newControl));
|
||||
|
||||
window.getFocusManager().requestFocus({ oldHandle, 0 });
|
||||
oldRaw->setFocusCallback([&](bool focused, StellarX::FocusReason)
|
||||
{
|
||||
if (!focused) newRaw->setFocusEnabledForTest(false);
|
||||
});
|
||||
const auto result = window.getFocusManager().requestFocus({ newHandle, 0 });
|
||||
|
||||
Assert::AreEqual(static_cast<int>(StellarX::FocusError::Disabled),
|
||||
static_cast<int>(result.error));
|
||||
Assert::IsFalse(newRaw->hasLogicalFocus());
|
||||
Assert::IsFalse(static_cast<bool>(window.getFocusManager().focusedTarget()));
|
||||
}
|
||||
|
||||
TEST_METHOD(FocusGainedCallbackCanDeleteItselfAndFallsBack)
|
||||
{
|
||||
Window window(200, 120, 1);
|
||||
auto fallback = std::make_unique<FocusTestControl>();
|
||||
FocusTestControl* fallbackRaw = fallback.get();
|
||||
const auto fallbackHandle = window.addControl(std::move(fallback));
|
||||
auto selfDeleting = std::make_unique<FocusTestControl>();
|
||||
FocusTestControl* deletingRaw = selfDeleting.get();
|
||||
const auto deletingHandle = window.addControl(std::move(selfDeleting));
|
||||
|
||||
window.getFocusManager().requestFocus({ fallbackHandle, 0 });
|
||||
deletingRaw->setFocusCallback([&](bool focused, StellarX::FocusReason)
|
||||
{
|
||||
if (focused) Assert::IsTrue(window.removeControl(deletingHandle));
|
||||
});
|
||||
const auto result = window.getFocusManager().requestFocus({ deletingHandle, 0 });
|
||||
|
||||
Assert::AreEqual(static_cast<int>(StellarX::FocusError::None),
|
||||
static_cast<int>(result.error));
|
||||
Assert::AreEqual(fallbackHandle.value,
|
||||
window.getFocusManager().focusedTarget().owner.value);
|
||||
Assert::IsTrue(fallbackRaw->hasLogicalFocus());
|
||||
Assert::IsNull(window.findControl(deletingHandle));
|
||||
}
|
||||
|
||||
TEST_METHOD(ReentrantPendingIsSingleSlotAndDoesNotReviveOlderRequest)
|
||||
{
|
||||
Window window(300, 120, 1);
|
||||
auto oldControl = std::make_unique<FocusTestControl>();
|
||||
FocusTestControl* oldRaw = oldControl.get();
|
||||
const auto oldHandle = window.addControl(std::move(oldControl));
|
||||
auto trigger = std::make_unique<FocusTestControl>();
|
||||
const auto triggerHandle = window.addControl(std::move(trigger));
|
||||
auto olderPending = std::make_unique<FocusTestControl>();
|
||||
FocusTestControl* olderRaw = olderPending.get();
|
||||
const auto olderHandle = window.addControl(std::move(olderPending));
|
||||
auto latestPending = std::make_unique<FocusTestControl>();
|
||||
const auto latestHandle = window.addControl(std::move(latestPending));
|
||||
|
||||
window.getFocusManager().requestFocus({ oldHandle, 0 });
|
||||
oldRaw->setFocusCallback([&](bool focused, StellarX::FocusReason)
|
||||
{
|
||||
if (focused) return;
|
||||
window.getFocusManager().requestFocus({ olderHandle, 0 });
|
||||
window.getFocusManager().requestFocus({ latestHandle, 0 });
|
||||
Assert::IsTrue(window.removeControl(latestHandle));
|
||||
});
|
||||
window.getFocusManager().requestFocus({ triggerHandle, 0 });
|
||||
|
||||
Assert::AreEqual(triggerHandle.value,
|
||||
window.getFocusManager().focusedTarget().owner.value);
|
||||
Assert::IsFalse(olderRaw->hasLogicalFocus());
|
||||
Assert::IsNull(window.findControl(latestHandle));
|
||||
}
|
||||
|
||||
TEST_METHOD(FocusCallbackCanPushScopeAndPopRestoresOuterFocus)
|
||||
{
|
||||
Window window(300, 160, 1);
|
||||
auto scopeRoot = std::make_unique<Canvas>(0, 40, 200, 100);
|
||||
const auto scopeRootHandle = scopeRoot->getHandle();
|
||||
auto scopedChild = std::make_unique<FocusTestControl>();
|
||||
FocusTestControl* childRaw = scopedChild.get();
|
||||
const auto childHandle = scopeRoot->addControl(std::move(scopedChild));
|
||||
window.addControl(std::move(scopeRoot));
|
||||
|
||||
auto outer = std::make_unique<FocusTestControl>();
|
||||
FocusTestControl* outerRaw = outer.get();
|
||||
const auto outerHandle = window.addControl(std::move(outer));
|
||||
StellarX::FocusScopeId pushed{};
|
||||
bool didPush = false;
|
||||
outerRaw->setFocusCallback([&](bool focused, StellarX::FocusReason)
|
||||
{
|
||||
if (focused && !didPush)
|
||||
{
|
||||
didPush = true;
|
||||
pushed = window.getFocusManager().pushScope(
|
||||
{ scopeRootHandle, 0 }, { childHandle, 0 });
|
||||
}
|
||||
});
|
||||
|
||||
window.getFocusManager().requestFocus({ outerHandle, 0 });
|
||||
Assert::IsTrue(static_cast<bool>(pushed));
|
||||
Assert::AreEqual(childHandle.value,
|
||||
window.getFocusManager().focusedTarget().owner.value);
|
||||
Assert::IsTrue(childRaw->hasLogicalFocus());
|
||||
Assert::IsTrue(window.getFocusManager().popScope(pushed));
|
||||
Assert::AreEqual(outerHandle.value,
|
||||
window.getFocusManager().focusedTarget().owner.value);
|
||||
Assert::IsTrue(outerRaw->hasLogicalFocus());
|
||||
}
|
||||
|
||||
TEST_METHOD(FocusLostCallbackScopeRestoresTheActualOldTarget)
|
||||
{
|
||||
Window window(360, 180, 1);
|
||||
auto unrelated = std::make_unique<FocusTestControl>();
|
||||
const auto unrelatedHandle = window.addControl(std::move(unrelated));
|
||||
auto oldControl = std::make_unique<FocusTestControl>();
|
||||
FocusTestControl* oldRaw = oldControl.get();
|
||||
const auto oldHandle = window.addControl(std::move(oldControl));
|
||||
auto requested = std::make_unique<FocusTestControl>();
|
||||
const auto requestedHandle = window.addControl(std::move(requested));
|
||||
|
||||
auto scopeRoot = std::make_unique<Canvas>(0, 40, 200, 100);
|
||||
const auto scopeRootHandle = scopeRoot->getHandle();
|
||||
auto scopedChild = std::make_unique<FocusTestControl>();
|
||||
const auto childHandle = scopeRoot->addControl(std::move(scopedChild));
|
||||
window.addControl(std::move(scopeRoot));
|
||||
|
||||
StellarX::FocusScopeId pushed{};
|
||||
oldRaw->setFocusCallback([&](bool focused, StellarX::FocusReason)
|
||||
{
|
||||
if (!focused && !pushed)
|
||||
pushed = window.getFocusManager().pushScope(
|
||||
{ scopeRootHandle, 0 }, { childHandle, 0 });
|
||||
});
|
||||
|
||||
window.getFocusManager().requestFocus({ oldHandle, 0 });
|
||||
window.getFocusManager().requestFocus({ requestedHandle, 0 });
|
||||
Assert::IsTrue(static_cast<bool>(pushed));
|
||||
Assert::AreEqual(childHandle.value,
|
||||
window.getFocusManager().focusedTarget().owner.value);
|
||||
Assert::IsTrue(window.getFocusManager().popScope(pushed));
|
||||
Assert::AreEqual(oldHandle.value,
|
||||
window.getFocusManager().focusedTarget().owner.value);
|
||||
Assert::AreNotEqual(unrelatedHandle.value,
|
||||
window.getFocusManager().focusedTarget().owner.value);
|
||||
}
|
||||
|
||||
TEST_METHOD(ReentrantFocusCycleStopsAtExplicitLimit)
|
||||
{
|
||||
Window window(200, 120, 1);
|
||||
auto first = std::make_unique<FocusTestControl>();
|
||||
FocusTestControl* firstRaw = first.get();
|
||||
const auto firstHandle = window.addControl(std::move(first));
|
||||
auto second = std::make_unique<FocusTestControl>();
|
||||
FocusTestControl* secondRaw = second.get();
|
||||
const auto secondHandle = window.addControl(std::move(second));
|
||||
|
||||
firstRaw->setFocusCallback([&](bool focused, StellarX::FocusReason)
|
||||
{
|
||||
if (focused) window.getFocusManager().requestFocus({ secondHandle, 0 });
|
||||
});
|
||||
secondRaw->setFocusCallback([&](bool focused, StellarX::FocusReason)
|
||||
{
|
||||
if (focused) window.getFocusManager().requestFocus({ firstHandle, 0 });
|
||||
});
|
||||
|
||||
const auto result = window.getFocusManager().requestFocus({ firstHandle, 0 });
|
||||
Assert::AreEqual(static_cast<int>(StellarX::FocusError::ReentrancyLimit),
|
||||
static_cast<int>(result.error));
|
||||
Assert::AreEqual(static_cast<int>(StellarX::FocusError::ReentrancyLimit),
|
||||
static_cast<int>(window.getFocusManager().getLastError()));
|
||||
Assert::IsTrue(static_cast<bool>(window.getFocusManager().focusedTarget()));
|
||||
}
|
||||
|
||||
TEST_METHOD(ActiveRemovalUsesNextTabStopFallback)
|
||||
{
|
||||
Window window(240, 120, 1);
|
||||
auto first = std::make_unique<FocusTestControl>(0);
|
||||
const auto firstHandle = window.addControl(std::move(first));
|
||||
auto second = std::make_unique<FocusTestControl>(1);
|
||||
const auto secondHandle = window.addControl(std::move(second));
|
||||
auto third = std::make_unique<FocusTestControl>(2);
|
||||
FocusTestControl* thirdRaw = third.get();
|
||||
const auto thirdHandle = window.addControl(std::move(third));
|
||||
|
||||
window.getFocusManager().requestFocus({ secondHandle, 0 });
|
||||
Assert::IsTrue(window.removeControl(secondHandle));
|
||||
Assert::AreEqual(thirdHandle.value,
|
||||
window.getFocusManager().focusedTarget().owner.value);
|
||||
Assert::IsTrue(thirdRaw->hasLogicalFocus());
|
||||
Assert::IsNotNull(window.findControl(firstHandle));
|
||||
}
|
||||
|
||||
TEST_METHOD(ClearInvalidatesWholeBatchBeforeChoosingFallback)
|
||||
{
|
||||
Window window(240, 120, 1);
|
||||
auto first = std::make_unique<FocusTestControl>();
|
||||
const auto firstHandle = window.addControl(std::move(first));
|
||||
int unexpectedGains = 0;
|
||||
auto second = std::make_unique<FocusTestControl>();
|
||||
second->setFocusCallback([&](bool focused, StellarX::FocusReason)
|
||||
{ if (focused) ++unexpectedGains; });
|
||||
window.addControl(std::move(second));
|
||||
auto third = std::make_unique<FocusTestControl>();
|
||||
third->setFocusCallback([&](bool focused, StellarX::FocusReason)
|
||||
{ if (focused) ++unexpectedGains; });
|
||||
window.addControl(std::move(third));
|
||||
|
||||
window.getFocusManager().requestFocus({ firstHandle, 0 });
|
||||
window.clearControls();
|
||||
|
||||
Assert::AreEqual(0, unexpectedGains);
|
||||
Assert::IsFalse(static_cast<bool>(window.getFocusManager().focusedTarget()));
|
||||
Assert::AreEqual<size_t>(0, window.getControlHandles().size());
|
||||
}
|
||||
|
||||
TEST_METHOD(TabControlExposesRealButtonHandleWithoutSyntheticSubNode)
|
||||
{
|
||||
Window window(300, 200, 1);
|
||||
auto tabs = std::make_unique<TabControl>(0, 0, 300, 200);
|
||||
auto tabButton = std::make_unique<Button>(0, 0, 80, 24, "tab");
|
||||
const auto tabHandle = tabButton->getHandle();
|
||||
tabs->addTab(std::move(tabButton), std::make_unique<Canvas>(0, 24, 300, 176));
|
||||
window.addControl(std::move(tabs));
|
||||
|
||||
const auto stops = window.getFocusManager().collectTabStops();
|
||||
auto found = std::find_if(stops.begin(), stops.end(), [&](const auto& item)
|
||||
{ return item.id.owner == tabHandle; });
|
||||
Assert::IsTrue(found != stops.end());
|
||||
Assert::AreEqual<std::uint32_t>(0, found->id.subNode);
|
||||
}
|
||||
|
||||
TEST_METHOD(TabControlExcludesInactivePageFromFocusTree)
|
||||
{
|
||||
Window window(320, 220, 1);
|
||||
auto tabs = std::make_unique<TabControl>(0, 0, 300, 200);
|
||||
|
||||
auto firstPage = std::make_unique<Canvas>(0, 24, 300, 176);
|
||||
Canvas* firstPageRaw = firstPage.get();
|
||||
auto firstChild = std::make_unique<FocusTestControl>();
|
||||
const auto firstChildHandle = firstPage->addControl(std::move(firstChild));
|
||||
tabs->addTab(std::make_unique<Button>(0, 0, 80, 24, "first"),
|
||||
std::move(firstPage));
|
||||
|
||||
auto secondPage = std::make_unique<Canvas>(0, 24, 300, 176);
|
||||
Canvas* secondPageRaw = secondPage.get();
|
||||
auto secondChild = std::make_unique<FocusTestControl>();
|
||||
const auto secondChildHandle = secondPage->addControl(std::move(secondChild));
|
||||
tabs->addTab(std::make_unique<Button>(80, 0, 80, 24, "second"),
|
||||
std::move(secondPage));
|
||||
window.addControl(std::move(tabs));
|
||||
|
||||
auto contains = [&](StellarX::ControlHandle handle)
|
||||
{
|
||||
const auto stops = window.getFocusManager().collectTabStops();
|
||||
return std::any_of(stops.begin(), stops.end(), [&](const auto& item)
|
||||
{ return item.id.owner == handle; });
|
||||
};
|
||||
|
||||
// addTab keeps pages hidden until the first real draw. Drive visibility directly here so
|
||||
// this non-GDI unit isolates the focus-tree filter rather than TabControl rendering.
|
||||
firstPageRaw->setIsVisible(true);
|
||||
Assert::IsTrue(contains(firstChildHandle));
|
||||
Assert::IsFalse(contains(secondChildHandle));
|
||||
window.getFocusManager().requestFocus({ firstChildHandle, 0 });
|
||||
firstPageRaw->setIsVisible(false);
|
||||
secondPageRaw->setIsVisible(true);
|
||||
Assert::IsFalse(contains(firstChildHandle));
|
||||
Assert::IsTrue(contains(secondChildHandle));
|
||||
Assert::AreNotEqual(firstChildHandle.value,
|
||||
window.getFocusManager().focusedTarget().owner.value);
|
||||
}
|
||||
};
|
||||
|
||||
// 验证 MBCS 文本所有权、空页签边界、提示延时和密码掩码等纯逻辑行为。
|
||||
TEST_CLASS(TextAndTabTests)
|
||||
{
|
||||
|
||||
@@ -251,6 +251,65 @@ int main(int argc, char** argv)
|
||||
});
|
||||
window.addControl(std::move(resize));
|
||||
}
|
||||
else if (scenario == "focus")
|
||||
{
|
||||
// Stage 2 integration: real focus visuals, Table child eligibility, and active Tab page filtering.
|
||||
auto button = MakeButton(20, 20, 120, 40, "focus button");
|
||||
Button* buttonRaw = button.get();
|
||||
bool buttonFocusObserved = false;
|
||||
button->setOnClickListener([&]() { buttonFocusObserved = buttonRaw->hasLogicalFocus(); });
|
||||
window.addControl(std::move(button));
|
||||
|
||||
auto textBox = std::make_unique<TextBox>(20, 80, 160, 36, "focus text");
|
||||
TextBox* textBoxRaw = textBox.get();
|
||||
window.addControl(std::move(textBox));
|
||||
|
||||
auto table = std::make_unique<Table>(210, 20);
|
||||
const auto tableHandle = table->getHandle();
|
||||
table->setHeaders({ "value" });
|
||||
table->setRowsPerPage(1);
|
||||
table->setData({ { "one" }, { "two" } });
|
||||
window.addControl(std::move(table));
|
||||
|
||||
auto tabs = std::make_unique<TabControl>(20, 145, 300, 140);
|
||||
tabs->setActiveIndex(0);
|
||||
auto firstPage = std::make_unique<Canvas>(0, 0, 300, 110);
|
||||
auto firstPageButton = MakeButton(10, 40, 110, 30, "active child");
|
||||
const auto firstPageChildHandle = firstPageButton->getHandle();
|
||||
firstPage->addControl(std::move(firstPageButton));
|
||||
tabs->addTab(MakeButton(0, 0, 100, 30, "first"), std::move(firstPage));
|
||||
|
||||
auto secondPage = std::make_unique<Canvas>(0, 0, 300, 110);
|
||||
auto secondPageButton = MakeButton(10, 40, 110, 30, "inactive child");
|
||||
const auto secondPageChildHandle = secondPageButton->getHandle();
|
||||
secondPage->addControl(std::move(secondPageButton));
|
||||
tabs->addTab(MakeButton(100, 0, 100, 30, "second"), std::move(secondPage));
|
||||
window.addControl(std::move(tabs));
|
||||
|
||||
auto verify = MakeButton(350, 260, 110, 36, "verify");
|
||||
verify->setOnClickListener([&, buttonRaw]() {
|
||||
const auto stops = window.getFocusManager().collectTabStops();
|
||||
std::size_t tableTabStops = 0;
|
||||
bool activePageChildPresent = false;
|
||||
bool inactivePageChildPresent = false;
|
||||
bool allSubNodesZero = true;
|
||||
for (const auto& stop : stops)
|
||||
{
|
||||
if (stop.logicalParent.owner == tableHandle) ++tableTabStops;
|
||||
if (stop.id.owner == firstPageChildHandle) activePageChildPresent = true;
|
||||
if (stop.id.owner == secondPageChildHandle) inactivePageChildPresent = true;
|
||||
if (stop.id.subNode != 0) allSubNodesZero = false;
|
||||
}
|
||||
WriteResult(resultDir, scenario,
|
||||
std::string("{\"buttonFocusObserved\":") + (buttonFocusObserved ? "true" : "false") +
|
||||
",\"tableTabStops\":" + std::to_string(tableTabStops) +
|
||||
",\"activePageChildPresent\":" + (activePageChildPresent ? "true" : "false") +
|
||||
",\"inactivePageChildPresent\":" + (inactivePageChildPresent ? "true" : "false") +
|
||||
",\"allSubNodesZero\":" + (allSubNodesZero ? "true" : "false") + "}");
|
||||
});
|
||||
window.addControl(std::move(verify));
|
||||
textBoxRaw->requestFocus();
|
||||
}
|
||||
else if (scenario == "dialog")
|
||||
{
|
||||
// 先叠加两个非模态对话框,再只关闭上层,验证下层恢复和交互顺序。
|
||||
|
||||
Reference in New Issue
Block a user