// Copyright (c) Microsoft Corporation. // Licensed under the MIT license. #include "pch.h" #include "TerminalPage.h" #include "Utils.h" #include "../../types/inc/utils.hpp" #include using namespace winrt; using namespace winrt::Windows::Foundation::Collections; using namespace winrt::Windows::UI::Xaml; using namespace winrt::Windows::UI::Xaml::Controls; using namespace winrt::Windows::UI::Core; using namespace winrt::Windows::System; using namespace winrt::Windows::ApplicationModel::DataTransfer; using namespace winrt::Windows::UI::Text; using namespace winrt::Microsoft::Terminal; using namespace winrt::Microsoft::Terminal::Control; using namespace winrt::Microsoft::Terminal::TerminalConnection; using namespace winrt::Microsoft::Terminal::Settings::Model; using namespace ::TerminalApp; using namespace ::Microsoft::Console; using namespace std::chrono_literals; namespace winrt { namespace MUX = Microsoft::UI::Xaml; namespace WUX = Windows::UI::Xaml; using IInspectable = Windows::Foundation::IInspectable; } namespace winrt::TerminalApp::implementation { // Method Description: // - Stop previewing the currently previewed action. We can use this to // clean up any state from that action's preview. // - We use _lastPreviewedCommand to determine what type of action to clean up. // Arguments: // - // Return Value: // - void TerminalPage::_EndPreview() { if (_lastPreviewedCommand == nullptr || _lastPreviewedCommand.ActionAndArgs() == nullptr) { return; } switch (_lastPreviewedCommand.ActionAndArgs().Action()) { case ShortcutAction::SetColorScheme: { _EndPreviewColorScheme(); break; } } _lastPreviewedCommand = nullptr; } // Method Description: // - Revert any changes from the preview on a SetColorScheme action. This // will remove the preview TerminalSettings we inserted into the control's // TerminalSettings graph, and update the control. // Arguments: // - // Return Value: // - void TerminalPage::_EndPreviewColorScheme() { for (const auto& f : _restorePreviewFuncs) { f(); } _restorePreviewFuncs.clear(); } // Method Description: // - Preview handler for the SetColorScheme action. // - This method will stash functions to reset the settings of the selected controls in // _restorePreviewFuncs. Then it will create a new TerminalSettings object // with only the properties from the ColorScheme set. It'll _insert_ a // TerminalSettings between the control's root settings (built from // CascadiaSettings) and the control's runtime settings. That'll cause the // control to use _that_ table as the active color scheme. // Arguments: // - args: The SetColorScheme args with the name of the scheme to use. // Return Value: // - void TerminalPage::_PreviewColorScheme(const Settings::Model::SetColorSchemeArgs& args) { if (const auto& scheme{ _settings.GlobalSettings().ColorSchemes().TryLookup(args.SchemeName()) }) { // Clear the saved preview funcs because we don't need to add a restore each time // the preview color changes, we only need to be able to restore the last one. _restorePreviewFuncs.clear(); _ApplyToActiveControls([&](const auto& control) { // Get the settings of the focused control and stash them const auto& controlSettings = control.Settings().as(); // Make sure to recurse up to the root - if you're doing // this while you're currently previewing a SetColorScheme // action, then the parent of the control's settings is _the // last preview TerminalSettings we inserted! We don't want // to save that one! auto originalSettings = controlSettings.GetParent(); while (originalSettings.GetParent() != nullptr) { originalSettings = originalSettings.GetParent(); } // Create a new child for those settings TerminalSettingsCreateResult fake{ originalSettings }; const auto& childStruct = TerminalSettings::CreateWithParent(fake); // Modify the child to have the applied color scheme childStruct.DefaultSettings().ApplyColorScheme(scheme); // Insert that new child as the parent of the control's settings controlSettings.SetParent(childStruct.DefaultSettings()); control.UpdateSettings(); // Take a copy of the inputs, since they are pointers anyways. _restorePreviewFuncs.emplace_back([=]() { // Get the runtime settings of the focused control const auto& controlSettings{ control.Settings().as() }; // Get the control's root settings, the ones that we actually // assigned to it. auto parentSettings{ controlSettings.GetParent() }; while (parentSettings.GetParent() != nullptr) { parentSettings = parentSettings.GetParent(); } // If the root settings are the same as the ones we stashed, // then reset the parent of the runtime settings to the stashed // settings. This condition might be false if the settings // hot-reloaded while the palette was open. In that case, we // don't want to reset the settings to what they were _before_ // the hot-reload. if (originalSettings == parentSettings) { // Set the original settings as the parent of the control's settings control.Settings().as().SetParent(originalSettings); } control.UpdateSettings(); }); }); } } // Method Description: // - Handler for the CommandPalette::PreviewAction event. The Command // Palette will raise this even when an action is selected, but _not_ // committed. This gives the Terminal a chance to display a "preview" of // the action. // - This will be called with a null args before an action is dispatched, or // when the palette is dismissed. // - For any actions that are to be previewed here, MAKE SURE TO RESTORE THE // STATE IN `TerminalPage::_EndPreview`. That method is called to revert // the terminal to the state it was in at the start of the preview. // - Currently, only SetColorScheme actions are preview-able. // Arguments: // - args: The Command that's trying to be previewed, or nullptr if we should stop the preview. // Return Value: // - void TerminalPage::_PreviewActionHandler(const IInspectable& /*sender*/, const Microsoft::Terminal::Settings::Model::Command& args) { if (args == nullptr || args.ActionAndArgs() == nullptr) { _EndPreview(); } else { switch (args.ActionAndArgs().Action()) { case ShortcutAction::SetColorScheme: { _PreviewColorScheme(args.ActionAndArgs().Args().try_as()); break; } } // GH#9818 Other ideas for actions that could be preview-able: // * Set Font size // * Set acrylic true/false/opacity? // * SetPixelShaderPath? // * SetWindowTheme (light/dark/system/)? // Stash this action, so we know what to do when we're done // previewing. _lastPreviewedCommand = args; } } }