CmdPal: Plain text viewer and image viewer IContent (#43964)

<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request

This PR introduces new types of IContent:  
- Plain text content – simple and straightforward, with options to
switch between UI and monospace fonts and toggle word wrap.
- It's super safe to display any random text content without having to
worry about escaping the pesky markdown.
- Image viewer content – a more polished way to display images:  
- When placed in the ContentPage, the component automatically resizes to
fit the viewport, ensuring the entire image is visible at once.
- Images can be opened in a built-in mini-viewer that lets you view,
pan, and zoom without leaving the Command Palette. (Doing this directly
on the page proved to be a UX and development headache.) Fully
keyboard-controllable, so there’s no need to take your hands off the
keys.

## Pictures? Pictures!

Plain text content:

<img width="960" height="604" alt="image"
src="https://github.com/user-attachments/assets/a4ec36f3-2f7f-4a2a-a646-53056c512023"
/>

Image viewer content:

<img width="939" height="605" alt="image"
src="https://github.com/user-attachments/assets/c87f5726-8cd0-4015-b2d9-f1457fa1ec96"
/>



https://github.com/user-attachments/assets/915cd9d2-e4e3-4baf-8df6-6a328a3592ba


<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #41038
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
This commit is contained in:
Jiří Polášek
2026-03-28 00:45:41 +01:00
committed by GitHub
parent 943c2a1ff5
commit 4cb3359314
37 changed files with 1752 additions and 76 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 760 KiB

View File

@@ -39,6 +39,62 @@ internal sealed partial class SampleContentPage : ContentPage
}
}
[SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "Sample code")]
internal sealed partial class SamplePlainTextContentPage : ContentPage
{
private readonly PlainTextContent _samplePlainText = new()
{
Text = """
# Sample Plain Text Content
This is a sample plain text content page.
You can right-click the content and switch wrap mode on or off, or change the font.
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
""",
};
private readonly PlainTextContent _samplePlainText2 = new()
{
Text = """
# Sample Plain Text Content
This is a sample plain text content page. This one is monospace and wraps by default.
You can right-click the content and switch wrap mode on or off, or change the font.
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
""",
FontFamily = FontFamily.Monospace,
WrapWords = true,
};
public override IContent[] GetContent() => [_samplePlainText, _samplePlainText2];
public SamplePlainTextContentPage()
{
Name = "Plain Text";
Title = "Sample Plain Text Content";
Icon = new IconInfo("\uE8D2"); // Text Document
}
}
[SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "Sample code")]
internal sealed partial class SampleImageContentPage : ContentPage
{
private readonly ImageContent _sampleImage = new(IconHelpers.FromRelativePath("Assets/Images/win-11-bloom-6k.jpg"));
private readonly ImageContent _sampleImage2 = new(IconHelpers.FromRelativePath("Assets/Images/win-11-bloom-6k.jpg")) { MaxWidth = 200, MaxHeight = 200 };
private readonly ImageContent _sampleImage3 = new(IconHelpers.FromRelativePath("Assets/Images/FluentEmojiChipmunk.svg")) { MaxWidth = 200, MaxHeight = 200 };
public override IContent[] GetContent() => [_sampleImage, _sampleImage2, _sampleImage3];
public SampleImageContentPage()
{
Name = "Image";
Title = "Sample Image Content";
Icon = new IconInfo("\uE722"); // Picture
}
}
[SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "Sample code")]
internal sealed partial class SampleContentForm : FormContent
{

View File

@@ -66,6 +66,16 @@ public partial class SamplesListPage : ListPage
Title = "Sample content page",
Subtitle = "Display mixed forms, markdown, and other types of content",
},
new ListItem(new SamplePlainTextContentPage())
{
Title = "Sample plain text content page",
Subtitle = "Display a page of plain text content",
},
new ListItem(new SampleImageContentPage())
{
Title = "Sample image content page",
Subtitle = "Display a page with an image",
},
new ListItem(new SampleTreeContentPage())
{
Title = "Sample nested content",