fb597ed304
## Summary of the Pull Request This PR adds support for renaming windows. ![window-renaming-000](https://user-images.githubusercontent.com/18356694/113034344-9a30be00-9157-11eb-9443-975f3c294f56.gif) ![window-renaming-001](https://user-images.githubusercontent.com/18356694/113034452-b5033280-9157-11eb-9e35-e5ac80fef0bc.gif) It does so through two new actions: * `renameWindow` takes a `name` parameter, and attempts to set the window's name to the provided name. This is useful if you always want to hit <kbd>F3</kbd> and rename a window to "foo" (READ: probably not that useful) * `openWindowRenamer` is more interesting: it opens a `TeachingTip` with a `TextBox`. When the user hits Ok, it'll request a rename for the provided value. This lets the user pick a new name for the window at runtime. In both cases, if there's already a window with that name, then the monarch will reject the rename, and pop a `Toast` in the window informing the user that the rename failed. Nifty! ## References * Builds on the toasts from #9523 * #5000 - process model megathread ## PR Checklist * [x] Closes https://github.com/microsoft/terminal/projects/5#card-50771747 * [x] I work here * [x] Tests addded (and pass with the help of #9660) * [ ] Requires documentation to be updated ## Detailed Description of the Pull Request / Additional comments I'm sending this PR while finishing up the tests. I figured I'll have time to sneak them in before I get the necessary reviews. > PAIN: We can't immediately focus the textbox in the TeachingTip. It's > not technically focusable until it is opened. However, it doesn't > provide an even tto tell us when it is opened. That's tracked in > microsoft/microsoft-ui-xaml#1607. So for now, the user _needs_ to > click on the text box manually. > We're also not using a ContentDialog for this, because in Xaml > Islands a text box in a ContentDialog won't recieve _any_ keypresses. > Fun! ## Validation Steps Performed I've been playing with ```json { "keys": "f1", "command": "identifyWindow" }, { "keys": "f2", "command": "identifyWindows" }, { "keys": "f3", "command": "openWindowRenamer" }, { "keys": "f4", "command": { "action": "renameWindow", "name": "foo" } }, { "keys": "f5", "command": { "action": "renameWindow", "name": "bar" } }, ``` and they seem to work as expected
198 lines
7.4 KiB
C++
198 lines
7.4 KiB
C++
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT license.
|
|
|
|
#include "pch.h"
|
|
#include "Peasant.h"
|
|
#include "CommandlineArgs.h"
|
|
#include "Peasant.g.cpp"
|
|
#include "../../types/inc/utils.hpp"
|
|
|
|
using namespace winrt;
|
|
using namespace winrt::Microsoft::Terminal;
|
|
using namespace winrt::Windows::Foundation;
|
|
using namespace ::Microsoft::Console;
|
|
|
|
namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|
{
|
|
Peasant::Peasant() :
|
|
_ourPID{ GetCurrentProcessId() }
|
|
{
|
|
}
|
|
|
|
// This is a private constructor to be used in unit tests, where we don't
|
|
// want each Peasant to necessarily use the current PID.
|
|
Peasant::Peasant(const uint64_t testPID) :
|
|
_ourPID{ testPID }
|
|
{
|
|
}
|
|
|
|
void Peasant::AssignID(uint64_t id)
|
|
{
|
|
_id = id;
|
|
}
|
|
uint64_t Peasant::GetID()
|
|
{
|
|
return _id;
|
|
}
|
|
|
|
uint64_t Peasant::GetPID()
|
|
{
|
|
return _ourPID;
|
|
}
|
|
|
|
bool Peasant::ExecuteCommandline(const Remoting::CommandlineArgs& args)
|
|
{
|
|
// If this is the first set of args we were ever told about, stash them
|
|
// away. We'll need to get at them later, when we setup the startup
|
|
// actions for the window.
|
|
if (_initialArgs == nullptr)
|
|
{
|
|
_initialArgs = args;
|
|
}
|
|
|
|
TraceLoggingWrite(g_hRemotingProvider,
|
|
"Peasant_ExecuteCommandline",
|
|
TraceLoggingUInt64(GetID(), "peasantID", "Our ID"),
|
|
TraceLoggingWideString(args.CurrentDirectory().c_str(), "directory", "the provided cwd"),
|
|
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
|
|
|
// Raise an event with these args. The AppHost will listen for this
|
|
// event to know when to take these args and dispatch them to a
|
|
// currently-running window.
|
|
_ExecuteCommandlineRequestedHandlers(*this, args);
|
|
|
|
return true;
|
|
}
|
|
|
|
Remoting::CommandlineArgs Peasant::InitialArgs()
|
|
{
|
|
return _initialArgs;
|
|
}
|
|
|
|
void Peasant::ActivateWindow(const Remoting::WindowActivatedArgs& args)
|
|
{
|
|
// TODO: projects/5 - somehow, pass an identifier for the current
|
|
// desktop into this method. The Peasant shouldn't need to be able to
|
|
// figure it out, but it will need to report it to the monarch.
|
|
|
|
// Store these new args as our last activated state. If a new monarch
|
|
// comes looking, we can use this info to tell them when we were last
|
|
// activated.
|
|
_lastActivatedArgs = args;
|
|
|
|
bool successfullyNotified = false;
|
|
// Raise our WindowActivated event, to let the monarch know we've been
|
|
// activated.
|
|
try
|
|
{
|
|
// Try/catch this, because the other side of this event is handled
|
|
// by the monarch. The monarch might have died. If they have, this
|
|
// will throw an exception. Just eat it, the election thread will
|
|
// handle hooking up the new one.
|
|
_WindowActivatedHandlers(*this, args);
|
|
successfullyNotified = true;
|
|
}
|
|
catch (...)
|
|
{
|
|
LOG_CAUGHT_EXCEPTION();
|
|
}
|
|
|
|
TraceLoggingWrite(g_hRemotingProvider,
|
|
"Peasant_ActivateWindow",
|
|
TraceLoggingUInt64(GetID(), "peasantID", "Our ID"),
|
|
TraceLoggingBoolean(successfullyNotified, "successfullyNotified", "true if we successfully notified the monarch"),
|
|
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
|
}
|
|
|
|
// Method Description:
|
|
// - Retrieve the WindowActivatedArgs describing the last activation of this
|
|
// peasant. New monarchs can use this state to determine when we were last
|
|
// activated.
|
|
// Arguments:
|
|
// - <none>
|
|
// Return Value:
|
|
// - a WindowActivatedArgs with info about when and where we were last activated.
|
|
Remoting::WindowActivatedArgs Peasant::GetLastActivatedArgs()
|
|
{
|
|
return _lastActivatedArgs;
|
|
}
|
|
|
|
// Method Description:
|
|
// - Tell this window to display it's window ID. We'll raise a
|
|
// DisplayWindowIdRequested event, which will get handled in the AppHost,
|
|
// and used to tell the app to display the ID toast.
|
|
// Arguments:
|
|
// - <none>
|
|
// Return Value:
|
|
// - <none>
|
|
void Peasant::DisplayWindowId()
|
|
{
|
|
// Not worried about try/catching this. The handler is in AppHost, which
|
|
// is in-proc for us.
|
|
_DisplayWindowIdRequestedHandlers(*this, nullptr);
|
|
}
|
|
|
|
// Method Description:
|
|
// - Raises an event to ask that all windows be identified. This will come
|
|
// back to us when the Monarch handles the event and calls our
|
|
// DisplayWindowId method.
|
|
// Arguments:
|
|
// - <none>
|
|
// Return Value:
|
|
// - <none>
|
|
void Peasant::RequestIdentifyWindows()
|
|
{
|
|
bool successfullyNotified = false;
|
|
|
|
try
|
|
{
|
|
// Try/catch this, because the other side of this event is handled
|
|
// by the monarch. The monarch might have died. If they have, this
|
|
// will throw an exception. Just eat it, the election thread will
|
|
// handle hooking up the new one.
|
|
_IdentifyWindowsRequestedHandlers(*this, nullptr);
|
|
successfullyNotified = true;
|
|
}
|
|
catch (...)
|
|
{
|
|
LOG_CAUGHT_EXCEPTION();
|
|
}
|
|
TraceLoggingWrite(g_hRemotingProvider,
|
|
"Peasant_RequestIdentifyWindows",
|
|
TraceLoggingUInt64(GetID(), "peasantID", "Our ID"),
|
|
TraceLoggingBoolean(successfullyNotified, "successfullyNotified", "true if we successfully notified the monarch"),
|
|
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
|
}
|
|
|
|
void Peasant::RequestRename(const winrt::Microsoft::Terminal::Remoting::RenameRequestArgs& args)
|
|
{
|
|
bool successfullyNotified = false;
|
|
const auto oldName{ _WindowName };
|
|
try
|
|
{
|
|
// Try/catch this, because the other side of this event is handled
|
|
// by the monarch. The monarch might have died. If they have, this
|
|
// will throw an exception. Just eat it, the election thread will
|
|
// handle hooking up the new one.
|
|
_RenameRequestedHandlers(*this, args);
|
|
if (args.Succeeded())
|
|
{
|
|
_WindowName = args.NewName();
|
|
}
|
|
successfullyNotified = true;
|
|
}
|
|
catch (...)
|
|
{
|
|
LOG_CAUGHT_EXCEPTION();
|
|
}
|
|
TraceLoggingWrite(g_hRemotingProvider,
|
|
"Peasant_RequestRename",
|
|
TraceLoggingUInt64(GetID(), "peasantID", "Our ID"),
|
|
TraceLoggingWideString(oldName.c_str(), "oldName", "Our old name"),
|
|
TraceLoggingWideString(args.NewName().c_str(), "newName", "The proposed name"),
|
|
TraceLoggingBoolean(args.Succeeded(), "succeeded", "true if the monarch ok'd this new name for us."),
|
|
TraceLoggingBoolean(successfullyNotified, "successfullyNotified", "true if we successfully notified the monarch"),
|
|
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
|
}
|
|
}
|