Repository: microsoft/terminal (language: cpp)

Issue:
Launch elevated Terminal profile without a keyboard
# Description of the new feature/enhancement
Today, there is no way to launch an elevated Terminal profile end-to-end without the need to use a keyboard. 

Even without a physical keyboard, the user flow is still the same with a on-screen keyboard: 
1) Launch Terminal
2) Ctrl + Click the profile you want to open

This Feature Request proposes a way for a user to open an elevated Terminal profile without the need to use a keyboard. 
This is a very common Accessibility scenario. 
The user should be able to launch an elevated Terminal profile with just a touch screen and / or mouse. 

For more details see: https://twitter.com/SBarizien/status/1599769771666391042 



Discussion / hints:
Were there any suggestions for how to do that? 

The only thing I could think of was some default nested actions for opening a new elevated profile in the Command Palette. Trick here is the wording - we don't want to open a new window always, but we'll need to if the terminal isn't already elevated. 

Maybe just "Open Elevated > {profiles}"?
As a workaround you can elevate current profile or execute command in elevated state with [gsudo](https://github.com/gerardog/gsudo/releases) via UAC confirmation.
@zadjii-msft I thought of this, which is everyone's favorite UI:

![image](https://user-images.githubusercontent.com/189190/206790820-903e0b70-5d67-4053-a334-9250ea9ae9b0.png)

It's a right-click menu item _with a right-click menu_. It also works with touch (touch-hold-release!)
If we did that, we could also put the splits in it...!
_can_ we add right click context menus to menu flyouts?
I gave it a shot in XAML Studio!

![image](https://user-images.githubusercontent.com/189190/206792681-b2e54028-cbf6-4db6-8cef-d085652be01d.png)

```xaml
        <Rectangle Width="100" Height="100" Fill="Yellow">
            <Rectangle.ContextFlyout>
                <MenuFlyout>
                    <MenuFlyoutItem Text="First" />
                    <MenuFlyoutItem Text="Second">
                        <MenuFlyoutItem.ContextFlyout>
                            <MenuFlyout>
                                <MenuFlyoutItem Text="Well I'll Be..." />
                            </MenuFlyout>
                        </MenuFlyoutItem.ContextFlyout>
                    </MenuFlyoutItem>
                </MenuFlyout>
            </Rectangle.ContextFlyout>
        </Rectangle>
```
Damn, yea let's do that. ez.
I've got a kind of fix for it. The tasks in Jumplist do not support custom menus, AFAIK. But I added the context menu to the profiles flyout. However, the context menu is wrong-positioned, and I have trouble determining why. It works correctly with localization and elevation; just the positioning is off. I used **BottomEdgeAlignedLeft** on **profileMenuItem** and experimented with all combinations, always getting it off.

![runasoff](https://user-images.githubusercontent.com/25628581/231909244-8632a357-3426-480a-b7e4-bd1e93bf07e2.png)


W E I R D. Xaml does have a tendency to do all sorts of weird stuff like that. Just recently I saw something weird like that with CommandBarFlyout where just switching to the Microsoft.UI.Xaml version (from Windows.UI.Xaml) magically fixed something. 

@michalnpl any chance you've got a branch to share? Maybe I could take a look. 
Sure @zadjii-msft  here is the [branch](/michalnpl/terminal/tree/michalnpl/Launch_elevated_Terminal_profile_without_a_keyboard)

Don't forget to regenerate resources (added items to .resw).

Interestingly Shift+F10 (shortcut to invoke context menu) opens the context menu in the right place while right-clicking on the item opens it completely misaligned.
@michalnpl Sorry this got lost in my list of open tabs 😅 

I'm betting that it's something with 

```c++
        runAsAdminContextMenu.ShowAt(profileMenuItem);
```

Like, I'd _hope_ that works. But I'd _bet_ that XAML islands does something weird with flyouts like that. I _know_ there were issues with DPI scaling of mouse events in XAML islands before, so I wouldn't be surprised here. 

This might be a crazy idea, but I bet you could just manually ask XAML to show the flyout at the cursor position. Something like:

https://github.com/microsoft/terminal/blob/ce60bf290a1750c9cf00d4fbe35715d946748d76/src/cascadia/TerminalControl/TermControl.cpp#L3386-L3404

but instead of a "control-relative position", use `CoreWindow::GetForCurrentThread().PointerPosition()`. That's my thought at least.
@zadjii-msft No worries. I thought about it but abandoned the idea because it seems like this should be handled correctly by XAML, and I am also afraid I'll miss some edge cases, or it will not be future-proof if I handle it manually. The proposed code seems to be doing the correct thing but the framework is acting unexpectedly. Perhaps there is a way to have the XAML team look at this?
I may take a stab at this if nobody is working it.
<img width="345" alt="image" src="https://github.com/microsoft/terminal/assets/2086722/05f648d7-ab99-49b1-9b75-3a5b0d0f11e2">

<img width="566" alt="image" src="https://github.com/microsoft/terminal/assets/2086722/200fd884-5bc6-49e7-9207-b7a57e43bf88">


Which alignment do we want? I can show screenshots of all of them if you'd want to see them all
I kinda like this one.

<img width="424" alt="image" src="https://github.com/microsoft/terminal/assets/2086722/5ad8584c-1803-430e-93b0-04b9f4f5cd8f">

Do we have a preference on which Icon we use for the LUA shield?  
<img width="360" alt="image" src="https://github.com/microsoft/terminal/assets/2086722/e527a2a4-55b9-41bf-a695-256a51d5112d">

Or this guy?

<img width="382" alt="image" src="https://github.com/microsoft/terminal/assets/2086722/25e5dac9-09b2-4f4d-a2d4-9d0e73d87d51">


Fix patch:
diff --git a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw
index 1a20db37695..80f0a9df574 100644
--- a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw
+++ b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw
@@ -216,7 +216,7 @@
   </data>
   <data name="SearchWebText" xml:space="preserve">
     <value>Web Search</value>
-  </data>  
+  </data>
   <data name="TabColorChoose" xml:space="preserve">
     <value>Color...</value>
   </data>
@@ -836,4 +836,8 @@
   <data name="MoveTabToNewWindowToolTip" xml:space="preserve">
     <value>Moves tab to a new window </value>
   </data>
-</root>
+  <data name="RunAsAdminFlyout.Text" xml:space="preserve">
+    <value>Run as Administrator</value>
+    <comment>This text is displayed on context menu for profile entries in add new tab button.</comment>
+  </data>
+</root>
\ No newline at end of file
diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp
index c0b3f0d5943..c4266413ddb 100644
--- a/src/cascadia/TerminalApp/TerminalPage.cpp
+++ b/src/cascadia/TerminalApp/TerminalPage.cpp
@@ -1057,6 +1057,15 @@ namespace winrt::TerminalApp::implementation
             }
         });
 
+        // Using the static method on the base class seems to do what we want in terms of placement.
+        WUX::Controls::Primitives::FlyoutBase::SetAttachedFlyout(profileMenuItem, _CreateRunAsAdminFlyout(profileIndex));
+
+        // Since we are not setting the ContextFlyout property of the item we have to handle the ContextRequested event
+        // and rely on the base class to show our menu.
+        profileMenuItem.ContextRequested([profileMenuItem](auto&&, auto&&) {
+            WUX::Controls::Primitives::FlyoutBase::ShowAttachedFlyout(profileMenuItem);
+        });
+
         return profileMenuItem;
     }
 
@@ -4899,4 +4908,42 @@ namespace winrt::TerminalApp::implementation
         // _RemoveTab will make sure to null out the _stashed.draggedTab
         _RemoveTab(*_stashed.draggedTab);
     }
+
+    /// <summary>
+    /// Creates a sub flyout menu for profile items in the split button menu that when clicked will show a menu item for
+    /// Run as Administrator
+    /// </summary>
+    /// <param name="profileIndex">The index for the profileMenuItem</param>
+    /// <returns>MenuFlyout that will show when the context is request on a profileMenuItem</returns>
+    WUX::Controls::MenuFlyout TerminalPage::_CreateRunAsAdminFlyout(int profileIndex)
+    {
+        // Create the MenuFlyout and set its placement
+        WUX::Controls::MenuFlyout profileMenuItemFlyout{};
+        profileMenuItemFlyout.Placement(WUX::Controls::Primitives::FlyoutPlacementMode::BottomEdgeAlignedRight);
+
+        // Create the menu item and an icon to use in the menu
+        WUX::Controls::MenuFlyoutItem runAsAdminItem{};
+        WUX::Controls::FontIcon adminShieldIcon{};
+
+        adminShieldIcon.Glyph(L"\xEA18");
+        adminShieldIcon.FontFamily(Media::FontFamily{ L"Segoe Fluent Icons, Segoe MDL2 Assets" });
+
+        runAsAdminItem.Icon(adminShieldIcon);
+        runAsAdminItem.Text(RS_(L"RunAsAdminFlyout/Text"));
+
+        // Click handler for the flyout item
+        runAsAdminItem.Click([profileIndex, weakThis{ get_weak() }](auto&&, auto&&) {
+            if (auto page{ weakThis.get() })
+            {
+                NewTerminalArgs args{ profileIndex };
+                args.Elevate(true);
+                page->_OpenNewTerminalViaDropdown(args);
+            }
+        });
+
+        profileMenuItemFlyout.Items().Append(runAsAdminItem);
+
+        return profileMenuItemFlyout;
+    }
+
 }
diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h
index bde39cca453..a7dee2e4ab6 100644
--- a/src/cascadia/TerminalApp/TerminalPage.h
+++ b/src/cascadia/TerminalApp/TerminalPage.h
@@ -530,7 +530,7 @@ namespace winrt::TerminalApp::implementation
         void _ContextMenuOpened(const IInspectable& sender, const IInspectable& args);
         void _SelectionMenuOpened(const IInspectable& sender, const IInspectable& args);
         void _PopulateContextMenu(const IInspectable& sender, const bool withSelection);
-
+        winrt::Windows::UI::Xaml::Controls::MenuFlyout _CreateRunAsAdminFlyout(int profileIndex);
 #pragma region ActionHandlers
         // These are all defined in AppActionHandlers.cpp
 #define ON_ALL_ACTIONS(action) DECLARE_ACTION_HANDLER(action);


Test patch: