diff --git a/SampleApps/WebView2APISample/ProcessComponent.cpp b/SampleApps/WebView2APISample/ProcessComponent.cpp index a5b9f85..18f8847 100644 --- a/SampleApps/WebView2APISample/ProcessComponent.cpp +++ b/SampleApps/WebView2APISample/ProcessComponent.cpp @@ -456,6 +456,9 @@ std::wstring ProcessComponent::ProcessFailedReasonToString( REASON_ENTRY(COREWEBVIEW2_PROCESS_FAILED_REASON_CRASHED); REASON_ENTRY(COREWEBVIEW2_PROCESS_FAILED_REASON_LAUNCH_FAILED); REASON_ENTRY(COREWEBVIEW2_PROCESS_FAILED_REASON_OUT_OF_MEMORY); + REASON_ENTRY(COREWEBVIEW2_PROCESS_FAILED_REASON_NORMAL_EXIT); + REASON_ENTRY(COREWEBVIEW2_PROCESS_FAILED_REASON_ABNORMAL_EXIT); + REASON_ENTRY(COREWEBVIEW2_PROCESS_FAILED_REASON_INTEGRITY_FAILURE); #undef REASON_ENTRY } diff --git a/SampleApps/WebView2APISample/ScenarioOriginConfigurationAPI.cpp b/SampleApps/WebView2APISample/ScenarioOriginConfigurationAPI.cpp new file mode 100644 index 0000000..ae5c356 --- /dev/null +++ b/SampleApps/WebView2APISample/ScenarioOriginConfigurationAPI.cpp @@ -0,0 +1,12 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "stdafx.h" + +#include +#include + +#include "CheckFailure.h" +#include "TextInputDialog.h" +#include "Util.h" diff --git a/SampleApps/WebView2APISample/ScenarioOriginConfigurationAPI.h b/SampleApps/WebView2APISample/ScenarioOriginConfigurationAPI.h new file mode 100644 index 0000000..2a9fc1a --- /dev/null +++ b/SampleApps/WebView2APISample/ScenarioOriginConfigurationAPI.h @@ -0,0 +1,5 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once diff --git a/SampleApps/WebView2APISample/TextInputDialog.cpp b/SampleApps/WebView2APISample/TextInputDialog.cpp index 5a39291..c8ce560 100644 --- a/SampleApps/WebView2APISample/TextInputDialog.cpp +++ b/SampleApps/WebView2APISample/TextInputDialog.cpp @@ -6,9 +6,187 @@ #include "TextInputDialog.h" +#include + #include "App.h" #include "resource.h" +// Maximum number of options per control group. Used to space control IDs so +// each group gets a non-overlapping range (e.g., group 0 = 5000-5009, +// group 1 = 5010-5019). +static constexpr int kMaxOptionsPerGroup = 10; + +// Heights for dynamic controls. +static constexpr int kCheckBoxHeight = 20; +static constexpr int kCheckBoxSpacing = 4; +static constexpr int kGroupLabelHeight = 18; +static constexpr int kLabelHeight = 100; +static constexpr int kTextAreaHeight = 120; +static constexpr int kControlSpacing = 16; +static constexpr int kLabelToInputSpacing = 6; + +namespace +{ + +// Returns the position of a child control in dialog-client coordinates. +RECT GetControlRect(HWND hDlg, HWND control) +{ + RECT rect; + GetWindowRect(control, &rect); + MapWindowPoints(NULL, hDlg, reinterpret_cast(&rect), 2); + return rect; +} + +// Shifts a button control by the given vertical offset. +void ShiftButton(HWND hDlg, int buttonId, int yOffset) +{ + HWND button = GetDlgItem(hDlg, buttonId); + RECT rect = GetControlRect(hDlg, button); + SetWindowPos(button, NULL, rect.left, rect.top + yOffset, 0, 0, SWP_NOSIZE | SWP_NOZORDER); +} + +// Applies the dialog font to a child control, if a font handle is available. +void SetControlFont(HWND ctrl, HFONT dialogFont) +{ + if (dialogFont) + { + SendMessage(ctrl, WM_SETFONT, reinterpret_cast(dialogFont), TRUE); + } +} + +// Creates a single BS_AUTOCHECKBOX BUTTON as a child of |hDlg|, assigns it +// the given |hmenu| ID, applies the dialog font, and pre-checks it if +// |isSelected| is true. Returns the checkbox HWND. +HWND CreateCheckBox( + HWND hDlg, PCWSTR label, HMENU hmenu, int x, int y, int width, HFONT dialogFont, + bool isSelected) +{ + HWND checkBox = CreateWindow( + L"BUTTON", label, WS_VISIBLE | WS_CHILD | BS_AUTOCHECKBOX, x, y, width, kCheckBoxHeight, + hDlg, hmenu, GetModuleHandle(NULL), NULL); + SetControlFont(checkBox, dialogFont); + if (isSelected) + { + SendMessage(checkBox, BM_SETCHECK, BST_CHECKED, 0); + } + return checkBox; +} + +// Creates a checkbox group: an optional STATIC label followed by one +// BS_AUTOCHECKBOX BUTTON per option. Each checkbox gets a unique control ID +// starting at |controlId| (incremented by 1 per option) so BN_CLICKED +// notifications can be mapped back to the correct option. Pre-checks any +// option whose isSelected is true. Returns total pixel height consumed. +int CreateCheckBoxGroup( + HWND hDlg, CheckBoxGroup& group, int controlId, int x, int y, int width, HFONT dialogFont) +{ + int totalHeight = 0; + + // Optional group label rendered as a STATIC text control. + if (!group.groupLabel.empty()) + { + // Create a plain text label above the checkboxes to describe the group. + HWND label = CreateWindow( + L"STATIC", group.groupLabel.c_str(), WS_VISIBLE | WS_CHILD, x, y, width, + kGroupLabelHeight, hDlg, NULL, GetModuleHandle(NULL), NULL); + SetControlFont(label, dialogFont); + totalHeight += kGroupLabelHeight + kCheckBoxSpacing; + } + + // Assert that the group fits within the kMaxOptionsPerGroup ID range. + assert(group.options.size() <= static_cast(kMaxOptionsPerGroup)); + for (size_t i = 0; i < group.options.size(); ++i) + { + HMENU hmenu = + reinterpret_cast(static_cast(controlId + static_cast(i))); + CreateCheckBox( + hDlg, group.options[i].label.c_str(), hmenu, x, y + totalHeight, width, dialogFont, + group.options[i].isSelected); + totalHeight += kCheckBoxHeight + kCheckBoxSpacing; + } + return totalHeight > 0 ? totalHeight - kCheckBoxSpacing : 0; +} + +// Creates a text area control: a read-only EDIT as a descriptive label (grey +// background, bordered, scrollable) followed by a writable EDIT below it for +// user input. The writable EDIT is assigned |controlId| so its text can be +// retrieved later via GetDlgItem. If |readOnly| is set, the input EDIT is +// made non-editable. Returns total pixel height consumed. +int CreateTextArea( + HWND hDlg, TextArea& textArea, int controlId, int x, int y, int width, HFONT dialogFont) +{ + HMENU hmenu = reinterpret_cast(static_cast(controlId)); + + // Read-only label EDIT — renders with a border and grey background. + // Uses an EDIT instead of STATIC so it supports scrolling for long text. + HWND label = CreateWindowEx( + WS_EX_CLIENTEDGE, L"EDIT", textArea.label.c_str(), + WS_VISIBLE | WS_CHILD | ES_READONLY | ES_MULTILINE | ES_AUTOVSCROLL | WS_VSCROLL, x, y, + width, kLabelHeight, hDlg, NULL, GetModuleHandle(NULL), NULL); + SetControlFont(label, dialogFont); + + // Writable input EDIT — placed below the label for user text entry. + HWND inputArea = CreateWindowEx( + WS_EX_CLIENTEDGE, L"EDIT", textArea.input.c_str(), + WS_VISIBLE | WS_CHILD | ES_MULTILINE | ES_AUTOVSCROLL, x, + y + kLabelHeight + kLabelToInputSpacing, width, kTextAreaHeight, hDlg, hmenu, + GetModuleHandle(NULL), NULL); + SetControlFont(inputArea, dialogFont); + if (textArea.readOnly) + { + SendMessage(inputArea, EM_SETREADONLY, TRUE, 0); + } + return kLabelHeight + kLabelToInputSpacing + kTextAreaHeight; +} + +// Dispatches to the appropriate creation helper based on the DialogControl +// variant type. Returns the pixel height consumed by the created control. +int CreateDynamicControl( + HWND hDlg, DialogControl& control, int controlId, int x, int y, int width, HFONT dialogFont) +{ + return std::visit( + [&](auto& ctrl) -> int + { + using T = std::decay_t; + if constexpr (std::is_same_v) + { + return CreateCheckBoxGroup(hDlg, ctrl, controlId, x, y, width, dialogFont); + } + else if constexpr (std::is_same_v) + { + return CreateTextArea(hDlg, ctrl, controlId, x, y, width, dialogFont); + } + }, + control); +} + +// Reads the check state of each checkbox in a group from its Win32 HWND and +// updates the corresponding option's isSelected flag. +void CollectCheckBoxGroupValues(HWND hDlg, CheckBoxGroup& group, int controlId) +{ + for (size_t j = 0; j < group.options.size(); ++j) + { + HWND ctrl = GetDlgItem(hDlg, controlId + static_cast(j)); + if (!ctrl) + continue; + group.options[j].isSelected = (SendMessage(ctrl, BM_GETCHECK, 0, 0) == BST_CHECKED); + } +} + +// Reads the user-entered text from a text area's writable EDIT control +// and stores it in the TextArea's |input| field. +void CollectTextAreaValue(HWND hDlg, TextArea& textArea, int controlId) +{ + HWND ctrl = GetDlgItem(hDlg, controlId); + if (!ctrl) + return; + int len = GetWindowTextLength(ctrl); + textArea.input.resize(len); + GetWindowText(ctrl, textArea.input.data(), len + 1); +} + +} // namespace + static INT_PTR CALLBACK DlgProcStatic( HWND hDlg, UINT message, @@ -20,6 +198,7 @@ static INT_PTR CALLBACK DlgProcStatic( switch (message) { case WM_INITDIALOG: + { self = (TextInputDialog*)lParam; SetWindowLongPtr(hDlg, GWLP_USERDATA, (LONG_PTR)self); @@ -31,23 +210,45 @@ static INT_PTR CALLBACK DlgProcStatic( { EnableWindow(GetDlgItem(hDlg, IDC_EDIT_INPUT), false); } + + self->CreateDynamicControls(hDlg); return (INT_PTR)TRUE; + } // TODO: don't close dialog if enter is pressed in edit control case WM_COMMAND: - if (self && LOWORD(wParam) == IDOK) + // Only handle button-click notifications (BN_CLICKED == 0). This also + // matches Enter key and dialog-manager-generated IDOK/IDCANCEL + // commands, which send HIWORD(wParam) == 0. Filtering here prevents + // spurious notifications like BN_SETFOCUS or BN_DOUBLECLICKED from + // triggering input collection or closing the dialog. + if (HIWORD(wParam) == BN_CLICKED) { - int length = GetWindowTextLength(GetDlgItem(hDlg, IDC_EDIT_INPUT)); - self->input.resize(length); - PWSTR data = const_cast(self->input.data()); - GetDlgItemText(hDlg, IDC_EDIT_INPUT, data, length + 1); - self->confirmed = true; - } + if (self) + { + // Handle checkbox clicks within checkbox groups. + if (!self->controls.empty()) + { + self->HandleCheckBoxClick(hDlg, LOWORD(wParam)); + } - if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) - { - SetWindowLongPtr(hDlg, GWLP_USERDATA, NULL); - EndDialog(hDlg, LOWORD(wParam)); - return (INT_PTR)TRUE; + if (LOWORD(wParam) == IDOK) + { + int length = GetWindowTextLength(GetDlgItem(hDlg, IDC_EDIT_INPUT)); + self->input.resize(length); + PWSTR data = const_cast(self->input.data()); + GetDlgItemText(hDlg, IDC_EDIT_INPUT, data, length + 1); + + self->CollectControlValues(hDlg); + self->confirmed = true; + } + } + + if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) + { + SetWindowLongPtr(hDlg, GWLP_USERDATA, NULL); + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + } } break; case WM_NCDESTROY: @@ -57,16 +258,185 @@ static INT_PTR CALLBACK DlgProcStatic( return (INT_PTR)FALSE; } +// --- TextInputDialog member methods --- + +// Existing public constructor - delegates to private extended constructor. +TextInputDialog::TextInputDialog( + HWND parent, PCWSTR title, PCWSTR prompt, PCWSTR description, + const std::wstring& defaultInput, bool readOnly) + : TextInputDialog(parent, title, prompt, description, defaultInput, readOnly, {}) +{ +} + +// Private extended constructor used by Builder and the public constructor. TextInputDialog::TextInputDialog( - HWND parent, - PCWSTR title, - PCWSTR prompt, - PCWSTR description, - const std::wstring& defaultInput, - bool readOnly) : - title(title), prompt(prompt), description(description), readOnly(readOnly), confirmed(false), input(defaultInput) + HWND parent, PCWSTR title, PCWSTR prompt, PCWSTR description, + const std::wstring& defaultInput, bool readOnly, std::vector controls) + : title(title), prompt(prompt), description(description), readOnly(readOnly), + confirmed(false), input(defaultInput), controls(std::move(controls)) { DialogBoxParam( g_hInstance, MAKEINTRESOURCE(IDD_DIALOG_INPUT), parent, DlgProcStatic, (LPARAM)this); } + +// Creates all dynamic controls and resizes the dialog to fit them. +// When the Builder path is used, the dialog template's built-in description +// and input edit controls are hidden (they are unused — all input comes from +// dynamic controls instead). Their vertical space is reclaimed so the dialog +// doesn't have a blank gap. +// Then iterates over the |controls| vector — each entry is a DialogControl +// variant (either a CheckBoxGroup or a TextArea). For each entry, +// computes a base control ID spaced by kMaxOptionsPerGroup so ID ranges don't +// overlap, then delegates to CreateDynamicControl which creates the Win32 +// child windows. After all controls are placed, expands the group box and +// dialog to fit, and shifts the OK/Cancel buttons down accordingly. +void TextInputDialog::CreateDynamicControls(HWND hDlg) +{ + if (controls.empty()) + return; + + // When dynamic controls are present (Builder path), hide the built-in + // description and input controls since all inputs are managed dynamically. + HWND descriptionEdit = GetDlgItem(hDlg, IDC_EDIT_DESCRIPTION); + HWND inputEdit = GetDlgItem(hDlg, IDC_EDIT_INPUT); + + // The prompt label is a group box. Place dynamic controls inside it. + HWND promptLabel = GetDlgItem(hDlg, IDC_STATIC_LABEL); + RECT promptRect = GetControlRect(hDlg, promptLabel); + + // Capture the original content height (group box top to input bottom) + // before hiding the built-in controls. + RECT inputRect = GetControlRect(hDlg, inputEdit); + int originalContentHeight = inputRect.bottom - promptRect.top; + + ShowWindow(descriptionEdit, SW_HIDE); + ShowWindow(inputEdit, SW_HIDE); + + // Inset controls inside the group box with padding. + static constexpr int kGroupBoxPadding = 10; + static constexpr int kGroupBoxTitleHeight = 20; + int controlX = promptRect.left + kGroupBoxPadding; + int controlWidth = (promptRect.right - promptRect.left) - 2 * kGroupBoxPadding; + int currentY = promptRect.top + kGroupBoxTitleHeight; + + HFONT dialogFont = reinterpret_cast(SendMessage(hDlg, WM_GETFONT, 0, 0)); + + for (size_t i = 0; i < controls.size(); ++i) + { + int controlId = IDC_DYNAMIC_CONTROL_BASE + static_cast(i) * kMaxOptionsPerGroup; + int height = CreateDynamicControl( + hDlg, controls[i], controlId, controlX, currentY, controlWidth, dialogFont); + currentY += height + kControlSpacing; + } + + // Expand the group box to contain all dynamic controls. + int newGroupBoxHeight = currentY - promptRect.top + kGroupBoxPadding; + SetWindowPos( + promptLabel, NULL, 0, 0, promptRect.right - promptRect.left, newGroupBoxHeight, + SWP_NOMOVE | SWP_NOZORDER); + + // Height delta: new group box height vs. original content area + // (group box + description + input edit that were replaced). + int additionalHeight = newGroupBoxHeight - originalContentHeight; + + // Grow or shrink the dialog by the computed delta. + // Uses screen-coordinate rect directly since we only need width/height. + RECT dlgRect; + GetWindowRect(hDlg, &dlgRect); + SetWindowPos( + hDlg, NULL, 0, 0, dlgRect.right - dlgRect.left, + dlgRect.bottom - dlgRect.top + additionalHeight, SWP_NOMOVE | SWP_NOZORDER); + + // Shift OK and Cancel buttons down by the same delta. + ShiftButton(hDlg, IDOK, additionalHeight); + ShiftButton(hDlg, IDCANCEL, additionalHeight); +} + +// Collects user-entered values from all dynamic controls into the |results| +// map, keyed by each control's label. Called when the user clicks OK. +// Iterates over |controls| using the same ID scheme as CreateDynamicControls +// (base + index * kMaxOptionsPerGroup). Dispatches to CollectCheckBoxGroupValues +// or CollectTextAreaValue for each control type, then stores the updated +// control in results via insert_or_assign. +void TextInputDialog::CollectControlValues(HWND hDlg) +{ + results.clear(); + for (size_t i = 0; i < controls.size(); ++i) + { + int controlId = IDC_DYNAMIC_CONTROL_BASE + static_cast(i) * kMaxOptionsPerGroup; + + std::visit( + [&](auto& control) + { + using T = std::decay_t; + if constexpr (std::is_same_v) + { + CollectCheckBoxGroupValues(hDlg, control, controlId); + results.insert_or_assign(control.groupLabel, control); + } + else if constexpr (std::is_same_v) + { + CollectTextAreaValue(hDlg, control, controlId); + results.insert_or_assign(control.label, control); + } + }, + controls[i]); + } +} + +// Handles a BN_CLICKED notification for a dynamic checkbox. Decodes the +// control ID to determine which group and option was clicked, then updates +// that option's isSelected flag from the checkbox's current check state. +// Uses early returns to keep the logic flat instead of deeply nested. +void TextInputDialog::HandleCheckBoxClick(HWND hDlg, int controlId) +{ + if (controlId < IDC_DYNAMIC_CONTROL_BASE) + return; + + int groupIndex = (controlId - IDC_DYNAMIC_CONTROL_BASE) / kMaxOptionsPerGroup; + int optionIndex = (controlId - IDC_DYNAMIC_CONTROL_BASE) % kMaxOptionsPerGroup; + + if (groupIndex < 0 || groupIndex >= static_cast(controls.size())) + return; + + auto* group = std::get_if(&controls[groupIndex]); + if (!group) + return; + + if (optionIndex < 0 || optionIndex >= static_cast(group->options.size())) + return; + + HWND checkBox = GetDlgItem(hDlg, controlId); + if (!checkBox) + return; + + group->options[optionIndex].isSelected = + (SendMessage(checkBox, BM_GETCHECK, 0, 0) == BST_CHECKED); +} + +// --- Builder implementation --- + +TextInputDialog::Builder::Builder(HWND parent, PCWSTR title, PCWSTR prompt) + : m_parent(parent), m_title(title), m_prompt(prompt) +{ +} + +TextInputDialog::Builder& TextInputDialog::Builder::AddCheckBoxGroup( + const std::wstring& groupLabel, std::vector options) +{ + m_controls.emplace_back(CheckBoxGroup(groupLabel, std::move(options))); + return *this; +} + +TextInputDialog::Builder& TextInputDialog::Builder::AddTextArea( + const std::wstring& label, const std::wstring& input, bool readOnly) +{ + m_controls.emplace_back(TextArea(label, input, readOnly)); + return *this; +} + +TextInputDialog TextInputDialog::Builder::Build() +{ + return TextInputDialog(m_parent, m_title, m_prompt, L"", L"", false, std::move(m_controls)); +} diff --git a/SampleApps/WebView2APISample/TextInputDialog.h b/SampleApps/WebView2APISample/TextInputDialog.h index be79ee4..f127de7 100644 --- a/SampleApps/WebView2APISample/TextInputDialog.h +++ b/SampleApps/WebView2APISample/TextInputDialog.h @@ -6,13 +6,98 @@ #include "stdafx.h" +#include #include +#include +#include +#include -// Constructing this struct will show a text input dialog and return when they user +// Per-type structs for dynamically created dialog controls. + +// A single checkbox option within a group. Holds a display label, an integer +// value returned to the caller when selected, and a flag indicating whether +// the checkbox should be pre-checked when the dialog is shown. +struct CheckBoxOption +{ + // The text displayed next to the checkbox. + std::wstring label; + // Application-defined integer identifying the feature this checkbox represents. + int value; + // Pre-checked on show if true; reflects user's final check state after OK. + bool isSelected; + + CheckBoxOption(const std::wstring& label, int value, bool isSelected = false) + : label(label), value(value), isSelected(isSelected) + { + } +}; + +// A group of related checkboxes rendered together under an optional label. +// Use this to present a set of feature toggles that the user can independently check or +// uncheck. Each option gets its own Win32 BUTTON control with a unique control ID. +struct CheckBoxGroup +{ + // Heading text above the checkboxes; if empty, no label is rendered. + std::wstring groupLabel; + // Checkbox options rendered top-to-bottom in vector order. + std::vector options; + + CheckBoxGroup(const std::wstring& groupLabel, std::vector options) + : groupLabel(groupLabel), options(std::move(options)) + { + } +}; + +// A multi-line text input area with a descriptive label above it. +// The label is rendered as a read-only EDIT (grey background, bordered) and +// the input area below it is a writable EDIT where the user can type. +// Set |readOnly| to true to make the input area non-editable (useful for +// displaying results). After the dialog is dismissed, |input| holds the +// text entered by the user. +struct TextArea +{ + // Descriptive text shown in a read-only EDIT above the input area. + std::wstring label; + // If true, the input area is non-editable (display-only). + bool readOnly; + // Pre-filled with default text; holds the user-entered text after OK. + std::wstring input; + + TextArea(const std::wstring& label, const std::wstring& input = L"", bool readOnly = false) + : label(label), readOnly(readOnly), input(input) + { + } +}; + +// Ordered collection of dynamic controls. Add new types to this variant. +using DialogControl = std::variant; + +// Constructing this struct will show a text input dialog and return when the user // dismisses it. If the user clicked the OK button, confirmed will be true and input will // be set to the input they entered. +// Use the Builder class for extended configuration (e.g., adding checkboxes, text areas). struct TextInputDialog { + // Builder for constructing a TextInputDialog with dynamic controls. + // When Builder is used, the built-in description and input controls are + // hidden; use AddTextArea and AddCheckBoxGroup to compose the dialog. + class Builder + { + public: + Builder(HWND parent, PCWSTR title, PCWSTR prompt); + Builder& AddCheckBoxGroup( + const std::wstring& groupLabel, std::vector options); + Builder& AddTextArea( + const std::wstring& label, const std::wstring& input = L"", bool readOnly = false); + TextInputDialog Build(); + + private: + HWND m_parent; + PCWSTR m_title; + PCWSTR m_prompt; + std::vector m_controls; + }; + TextInputDialog( HWND parent, PCWSTR title, @@ -21,11 +106,45 @@ struct TextInputDialog const std::wstring& defaultInput = L"", bool readOnly = false); + // The title displayed in the dialog's title bar. PCWSTR title; + // The prompt shown as the group box title in the dialog. PCWSTR prompt; + // TODO(task.ms/61534504): Use Builder method for creating TextInputDialog in win32 sample + // app + // Additional descriptive text shown in the dialog. PCWSTR description; + // Whether the text input field is read-only. bool readOnly; + // True if the user clicked OK to dismiss the dialog. bool confirmed; + // The text entered by the user in the input field. std::wstring input; + + // Extended fields populated via Builder. + // The dynamic controls (checkboxes, text areas) added via Builder. + std::vector controls; + // Unified result map populated on OK, keyed by control label (group label + // for checkbox groups, text area label for text areas). Each value is the + // DialogControl variant with its state updated: + // - CheckBoxGroup: each option's isSelected reflects the user's + // choice. Iterate options to find checked values. + // - TextArea: |input| holds the user-entered text. + // Use std::get or std::get