Skip to content

Commit

Permalink
Azure: display the user's options and additional information in color
Browse files Browse the repository at this point in the history
This commit colorizes parts of the AzCon's strings that include "user
options" -- things the user can type -- in yellow. This is to help with
accessibility.

The implementation here is based on a discussion with the team.
Alternative options for coloration were investigated, such as:

* Embedding escape sequences in the resource file.
  This would have been confusing for translators.
  The RESW file format doesn't support  escapes, so we would need
  some magic post-processing.
* Embedding "markup" in the resource file (like #{93m}, ...)
  This still would have been annoying for translators.

We settled on an implementation that takes resource names, colorizes
them, and string-formats them into other resources.
  • Loading branch information
DHowett committed Feb 29, 2020
1 parent b6aaf6a commit e10dec7
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 23 deletions.
40 changes: 31 additions & 9 deletions src/cascadia/TerminalConnection/AzureConnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,29 @@ static constexpr auto HttpUserAgent = L"Terminal/0.0";
} \
} while (0, 0)

static constexpr int USER_INPUT_COLOR = 93; // yellow - the color of something the user can type
static constexpr int USER_INFO_COLOR = 97; // white - the color of clarifying information

static inline std::wstring _colorize(const unsigned int colorCode, const std::wstring_view text)
{
return wil::str_printf<std::wstring>(L"\x1b[%um%.*s\x1b[m", colorCode, gsl::narrow_cast<size_t>(text.size()), text.data());
}

// Takes N resource names, loads the first one as a format string, and then
// loads all the remaining ones into the %s arguments in the first one after
// colorizing them in the USER_INPUT_COLOR.
// This is intended to be used to drop UserEntry resources into an existing string.
template<typename... Args>
static inline std::wstring _formatResWithColoredUserInputOptions(const std::wstring_view resourceKey, Args&&... args)
{
return wil::str_printf<std::wstring>(GetLibraryResourceString(resourceKey).data(), (_colorize(USER_INPUT_COLOR, GetLibraryResourceString(args)).data())...);
}

static inline std::wstring _formatTenantLine(int tenantNumber, const std::wstring_view tenantName, const std::wstring_view tenantID)
{
return wil::str_printf<std::wstring>(RS_(L"AzureIthTenant").data(), _colorize(USER_INPUT_COLOR, std::to_wstring(tenantNumber)).data(), _colorize(USER_INFO_COLOR, tenantName).data(), tenantID.data());
}

namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
{
// This function exists because the clientID only gets added by the release pipelines
Expand All @@ -63,7 +86,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
// - helper that will write an unterminated string (generally, from a resource) to the output stream.
// Arguments:
// - str: the string to write.
void AzureConnection::_WriteStringWithNewline(const winrt::hstring& str)
void AzureConnection::_WriteStringWithNewline(const std::wstring_view str)
{
_TerminalOutputHandlers(str + L"\r\n");
}
Expand Down Expand Up @@ -399,7 +422,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
continue;
}

winrt::hstring tenantLine{ wil::str_printf<std::wstring>(RS_(L"AzureIthTenant").c_str(), numTenants, nameJson.at(L"displayName").as_string().c_str(), nameJson.at(L"tenantID").as_string().c_str()) };
_WriteStringWithNewline(_formatTenantLine(numTenants, nameJson.at(L"displayName").as_string(), nameJson.at(L"tenantID").as_string()));
numTenants++;
}

Expand All @@ -415,8 +438,8 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
}

_WriteStringWithNewline(RS_(L"AzureEnterTenant"));
_WriteStringWithNewline(RS_(L"AzureNewLogin"));
_WriteStringWithNewline(RS_(L"AzureRemoveStored"));
_WriteStringWithNewline(_formatResWithColoredUserInputOptions(USES_RESOURCE(L"AzureNewLogin"), USES_RESOURCE(L"AzureUserEntry_NewLogin")));
_WriteStringWithNewline(_formatResWithColoredUserInputOptions(USES_RESOURCE(L"AzureRemoveStored"), USES_RESOURCE(L"AzureUserEntry_RemoveStored")));

int selectedTenant{ -1 };
do
Expand Down Expand Up @@ -457,7 +480,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
}

// if we got here, we didn't break out of the loop early and need to go 'round again
_WriteStringWithNewline(RS_(L"AzureInvalidAccessInput"));
_WriteStringWithNewline(_formatResWithColoredUserInputOptions(USES_RESOURCE(L"AzureInvalidAccessInput"), USES_RESOURCE(L"AzureUserEntry_NewLogin"), USES_RESOURCE(L"AzureUserEntry_RemoveStored")));
} while (true);

// User wants to login with one of the saved connection settings
Expand Down Expand Up @@ -568,8 +591,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
{
const auto& tenant = tenantListAsArray.at(i);
const auto [tenantId, tenantDisplayName] = _crackTenant(tenant);
winrt::hstring tenantLine{ wil::str_printf<std::wstring>(RS_(L"AzureIthTenant").c_str(), i, tenantDisplayName.c_str(), tenantId.c_str()) };
_WriteStringWithNewline(tenantLine);
_WriteStringWithNewline(_formatTenantLine(i, tenantDisplayName, tenantId));
}
_WriteStringWithNewline(RS_(L"AzureEnterTenant"));

Expand Down Expand Up @@ -622,7 +644,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
// - S_OK otherwise
HRESULT AzureConnection::_StoreHelper()
{
_WriteStringWithNewline(RS_(L"AzureStorePrompt"));
_WriteStringWithNewline(_formatResWithColoredUserInputOptions(USES_RESOURCE(L"AzureStorePrompt"), USES_RESOURCE(L"AzureUserEntry_Yes"), USES_RESOURCE(L"AzureUserEntry_No")));
// Wait for user input
do
{
Expand All @@ -642,7 +664,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
}

// if we got here, we didn't break out of the loop early and need to go 'round again
_WriteStringWithNewline(RS_(L"AzureInvalidStoreInput"));
_WriteStringWithNewline(_formatResWithColoredUserInputOptions(USES_RESOURCE(L"AzureInvalidStoreInput"), USES_RESOURCE(L"AzureUserEntry_Yes"), USES_RESOURCE(L"AzureUserEntry_No")));
} while (true);

_state = AzureState::TermConnecting;
Expand Down
2 changes: 1 addition & 1 deletion src/cascadia/TerminalConnection/AzureConnection.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
utility::string_t _cloudShellUri;
utility::string_t _terminalID;

void _WriteStringWithNewline(const winrt::hstring& str);
void _WriteStringWithNewline(const std::wstring_view str);
web::json::value _RequestHelper(web::http::client::http_client theClient, web::http::http_request theRequest);
web::json::value _GetDeviceCode();
web::json::value _WaitForUser(utility::string_t deviceCode, int pollInterval, int expiresIn);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,14 +123,14 @@
<data name="AzureEnterTenant" xml:space="preserve">
<value>Please enter the desired tenant number.</value>
</data>
<data name="AzureNewLogin" xml:space="preserve">
<value>Enter "n" to login with a different account.</value>
<data name="AzureNewLogin" xml:space="default">
<value>Enter %s to login with a different account</value>
</data>
<data name="AzureRemoveStored" xml:space="preserve">
<value>Enter "r" to remove the above saved connection settings.</value>
<data name="AzureRemoveStored" xml:space="default">
<value>Enter %s to remove the above saved connection settings.</value>
</data>
<data name="AzureInvalidAccessInput" xml:space="preserve">
<value>Please enter a valid number to access the stored connection settings, n to make a new one, or r to remove the stored ones.</value>
<data name="AzureInvalidAccessInput" xml:space="default">
<value>Please enter a valid number to access the stored connection settings, %s to make a new one, or %s to remove the stored ones.</value>
</data>
<data name="AzureNonNumberError" xml:space="preserve">
<value>Please enter a number.</value>
Expand All @@ -144,11 +144,11 @@
<data name="AzureNoCloudAccount" xml:space="preserve">
<value>You have not set up your cloud shell account yet. Please go to https://shell.azure.com to set it up.</value>
</data>
<data name="AzureStorePrompt" xml:space="preserve">
<value>Do you want to save these connection settings for future logins? [y/n]</value>
<data name="AzureStorePrompt" xml:space="default">
<value>Do you want to save these connection settings for future logins? [%s/%s]</value>
</data>
<data name="AzureInvalidStoreInput" xml:space="preserve">
<value>Please enter y or n</value>
<data name="AzureInvalidStoreInput" xml:space="default">
<value>Please enter %s or %s</value>
</data>
<data name="AzureRequestingCloud" xml:space="preserve">
<value>Requesting a cloud shell instance...</value>
Expand Down Expand Up @@ -183,8 +183,8 @@
<data name="AzureUnknownTenantName" xml:space="preserve">
<value>&lt;unknown tenant name&gt;</value>
</data>
<data name="AzureIthTenant" xml:space="preserve">
<value>Tenant %d: %s (%s)</value>
<data name="AzureIthTenant" xml:space="default">
<value>Tenant %s: %s (%s)</value>
</data>
<data name="AzureSuccessfullyAuthenticated" xml:space="preserve">
<value>Authenticated.</value>
Expand Down Expand Up @@ -217,4 +217,4 @@ If this resource spans multiple lines, it will not be displayed properly. Yeah.<
<data name="TelnetInternetOrServerIssue" xml:space="preserve">
<value>Could not connect to telnet server.</value>
</data>
</root>
</root>

0 comments on commit e10dec7

Please sign in to comment.