terminal/src/cascadia/TerminalApp/TerminalPage.h
Dustin L. Howett (MSFT) 15505337d8
Introduce a WinRT utils library and "checked resources" (#3350)
This commit introduces a C++/WinRT utility library and moves
ScopedResourceLoader into it. I decided to get uppity and introduce
something I like to call "checked resources." The idea is that every
resource reference from a library is knowable at compile time, and we
should be able to statically ensure that all resources exist.

This is a system that lets us immediately failfast (on launch) when a
library makes a static reference to a resource that doesn't exist at
runtime.

It exposes two new (preprocessor) APIs:
* `RS_(wchar_t)`: loads a localizable string resource by name.
* `USES_RESOURCE(wchar_t)`: marks a resource key as used, but is intended
  for loading images or passing static resource keys as parameters to
  functions that will look them up later.

Resource checking relies on diligent use of `USES_RESOURCE()` and `RS_()`
(which uses `USES_RESOURCE`), but can make sure we don't ship something
that'll blow up at runtime.

It works like this:

**IN DEBUG MODE**
- All resource names referenced through `USES_RESOURCE()` are emitted
  alongside their referencing filenames and line numbers into a static
  section of the binary.
  That section is named `.util$res$m`.

- We emit two sentinel values into two different sections, `.util$res$a`
  and `.util$res$z`.

- The linker sorts all sections alphabetically before crushing them
  together into the final binary.

- When we first construct a library's scoped resource loader, we
  iterate over every resource reference between `$a` and `$z` and check
  residency.

**IN RELEASE MODE**
- All checked resource code is compiled out.

Fixes #2146.

Macros are the only way to do something this cool, incidentally.

## Validation Steps Performed
Made references to a bunch of bad resources, tried to break it a lot.

It looks like this when it fails:

### App.cpp
```
36  static const std::array<std::wstring_view, 2> settingsLoadErrorsLabels {
37      USES_RESOURCE(L"NoProfilesText"),
38      USES_RESOURCE(L"AllProfilesHiddenText_HA_JUST_KIDDING")
39  };
```

```
WinRTUtils\LibraryResources.cpp(68)\TerminalApp.dll:
    FailFast(1) tid(1034) 8000FFFF Catastrophic failure
    Msg:[Resource AllProfilesHiddenText_HA_JUST_KIDDING not found in
      scope TerminalApp/Resources (App.cpp:38)] [EnsureAllResourcesArePresent]
```
2019-11-01 15:47:05 -07:00

157 lines
8.8 KiB
C++

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include "winrt/Microsoft.UI.Xaml.Controls.h"
#include "TerminalPage.g.h"
#include "Tab.h"
#include "CascadiaSettings.h"
#include "Profile.h"
#include <winrt/Microsoft.Terminal.TerminalControl.h>
#include <winrt/Microsoft.Terminal.TerminalConnection.h>
#include <winrt/Microsoft.UI.Xaml.Controls.Primitives.h>
#include <winrt/Windows.ApplicationModel.DataTransfer.h>
#include <winrt/Microsoft.UI.Xaml.XamlTypeInfo.h>
namespace winrt::TerminalApp::implementation
{
struct TerminalPage : TerminalPageT<TerminalPage>
{
public:
TerminalPage();
void SetSettings(std::shared_ptr<::TerminalApp::CascadiaSettings> settings, bool needRefreshUI);
void Create();
hstring Title();
void ShowOkDialog(const winrt::hstring& titleKey, const winrt::hstring& contentKey);
void TitlebarClicked();
void CloseWindow();
// -------------------------------- WinRT Events ---------------------------------
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(TitleChanged, _titleChangeHandlers, winrt::Windows::Foundation::IInspectable, winrt::hstring);
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(LastTabClosed, _lastTabClosedHandlers, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::LastTabClosedEventArgs);
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(SetTitleBarContent, _setTitleBarContentHandlers, winrt::Windows::Foundation::IInspectable, winrt::Windows::UI::Xaml::UIElement);
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(ShowDialog, _showDialogHandlers, winrt::Windows::Foundation::IInspectable, winrt::Windows::UI::Xaml::Controls::ContentDialog);
private:
// If you add controls here, but forget to null them either here or in
// the ctor, you're going to have a bad time. It'll mysteriously fail to
// activate the app.
// ALSO: If you add any UIElements as roots here, make sure they're
// updated in App::_ApplyTheme. The roots currently is _tabRow
// (which is a root when the tabs are in the titlebar.)
Microsoft::UI::Xaml::Controls::TabView _tabView{ nullptr };
TerminalApp::TabRowControl _tabRow{ nullptr };
Windows::UI::Xaml::Controls::Grid _tabContent{ nullptr };
Microsoft::UI::Xaml::Controls::SplitButton _newTabButton{ nullptr };
std::shared_ptr<::TerminalApp::CascadiaSettings> _settings{ nullptr };
std::vector<std::shared_ptr<Tab>> _tabs;
void _ShowAboutDialog();
void _ShowCloseWarningDialog();
void _CreateNewTabFlyout();
void _OpenNewTabDropdown();
void _OpenNewTab(std::optional<int> profileIndex);
void _CreateNewTabFromSettings(GUID profileGuid, winrt::Microsoft::Terminal::Settings::TerminalSettings settings);
winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _CreateConnectionFromSettings(GUID profileGuid, winrt::Microsoft::Terminal::Settings::TerminalSettings settings);
void _SettingsButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
void _FeedbackButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
void _AboutButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
void _CloseWarningPrimaryButtonOnClick(Windows::UI::Xaml::Controls::ContentDialog sender, Windows::UI::Xaml::Controls::ContentDialogButtonClickEventArgs eventArgs);
void _HookupKeyBindings(TerminalApp::AppKeyBindings bindings) noexcept;
void _UpdateTitle(std::shared_ptr<Tab> tab);
void _UpdateTabIcon(std::shared_ptr<Tab> tab);
void _UpdateTabView();
void _DuplicateTabViewItem();
void _RemoveTabViewItem(const Microsoft::UI::Xaml::Controls::TabViewItem& tabViewItem);
void _RemoveTabViewItemByIndex(uint32_t tabIndex);
void _RegisterTerminalEvents(Microsoft::Terminal::TerminalControl::TermControl term, std::shared_ptr<Tab> hostingTab);
void _SelectNextTab(const bool bMoveRight);
bool _SelectTab(const int tabIndex);
void _MoveFocus(const Direction& direction);
winrt::Microsoft::Terminal::TerminalControl::TermControl _GetFocusedControl();
int _GetFocusedTabIndex() const;
void _SetFocusedTabIndex(int tabIndex);
void _CloseFocusedTab();
void _CloseFocusedPane();
void _CloseAllTabs();
// Todo: add more event implementations here
// MSFT:20641986: Add keybindings for New Window
void _Scroll(int delta);
void _SplitVertical(const std::optional<GUID>& profileGuid);
void _SplitHorizontal(const std::optional<GUID>& profileGuid);
void _SplitPane(const Pane::SplitState splitType, const std::optional<GUID>& profileGuid);
void _ResizePane(const Direction& direction);
void _ScrollPage(int delta);
void _SetAcceleratorForMenuItem(Windows::UI::Xaml::Controls::MenuFlyoutItem& menuItem, const winrt::Microsoft::Terminal::Settings::KeyChord& keyChord);
void _CopyToClipboardHandler(const IInspectable& sender, const winrt::Microsoft::Terminal::TerminalControl::CopyToClipboardEventArgs& copiedData);
void _PasteFromClipboardHandler(const IInspectable& sender,
const Microsoft::Terminal::TerminalControl::PasteFromClipboardEventArgs& eventArgs);
bool _CopyText(const bool trimTrailingWhitespace);
void _PasteText();
static fire_and_forget PasteFromClipboard(winrt::Microsoft::Terminal::TerminalControl::PasteFromClipboardEventArgs eventArgs);
fire_and_forget _LaunchSettings(const bool openDefaults);
void _OnTabClick(const IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& eventArgs);
void _OnTabSelectionChanged(const IInspectable& sender, const Windows::UI::Xaml::Controls::SelectionChangedEventArgs& eventArgs);
void _OnTabItemsChanged(const IInspectable& sender, const Windows::Foundation::Collections::IVectorChangedEventArgs& eventArgs);
void _OnContentSizeChanged(const IInspectable& /*sender*/, Windows::UI::Xaml::SizeChangedEventArgs const& e);
void _OnTabCloseRequested(const IInspectable& sender, const Microsoft::UI::Xaml::Controls::TabViewTabCloseRequestedEventArgs& eventArgs);
void _RefreshUIForSettingsReload();
#pragma region ActionHandlers
// These are all defined in AppActionHandlers.cpp
void _HandleNewTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleOpenNewTabDropdown(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleDuplicateTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleCloseTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleClosePane(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleScrollUp(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleScrollDown(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleNextTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandlePrevTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleSplitVertical(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleSplitHorizontal(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleScrollUpPage(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleScrollDownPage(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleOpenSettings(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandlePasteText(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleNewTabWithProfile(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleSwitchToTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleResizePane(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleMoveFocus(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleCopyText(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleCloseWindow(const IInspectable&, const TerminalApp::ActionEventArgs& args);
void _HandleAdjustFontSize(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
#pragma endregion
};
}
namespace winrt::TerminalApp::factory_implementation
{
struct TerminalPage : TerminalPageT<TerminalPage, implementation::TerminalPage>
{
};
}