Skip to content

Commit

Permalink
[AdvancedPaste] Additional actions, including Image to text
Browse files Browse the repository at this point in the history
  • Loading branch information
drawbyperpetual committed Sep 11, 2024
1 parent d42cd4b commit e79d86d
Show file tree
Hide file tree
Showing 31 changed files with 799 additions and 217 deletions.
4 changes: 4 additions & 0 deletions src/common/interop/Constants.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ namespace winrt::PowerToys::Interop::implementation
{
return CommonSharedConstants::ADVANCED_PASTE_JSON_MESSAGE;
}
hstring Constants::AdvancedPasteAdditionalActionMessage()
{
return CommonSharedConstants::ADVANCED_PASTE_ADDITIONAL_ACTION_MESSAGE;
}
hstring Constants::AdvancedPasteCustomActionMessage()
{
return CommonSharedConstants::ADVANCED_PASTE_CUSTOM_ACTION_MESSAGE;
Expand Down
1 change: 1 addition & 0 deletions src/common/interop/Constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ namespace winrt::PowerToys::Interop::implementation
static hstring AdvancedPasteShowUIMessage();
static hstring AdvancedPasteMarkdownMessage();
static hstring AdvancedPasteJsonMessage();
static hstring AdvancedPasteAdditionalActionMessage();
static hstring AdvancedPasteCustomActionMessage();
static hstring ShowPowerOCRSharedEvent();
static hstring MouseJumpShowPreviewEvent();
Expand Down
1 change: 1 addition & 0 deletions src/common/interop/Constants.idl
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ namespace PowerToys
static String AdvancedPasteShowUIMessage();
static String AdvancedPasteMarkdownMessage();
static String AdvancedPasteJsonMessage();
static String AdvancedPasteAdditionalActionMessage();
static String AdvancedPasteCustomActionMessage();
static String ShowPowerOCRSharedEvent();
static String MouseJumpShowPreviewEvent();
Expand Down
2 changes: 2 additions & 0 deletions src/common/interop/shared_constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ namespace CommonSharedConstants

const wchar_t ADVANCED_PASTE_JSON_MESSAGE[] = L"PasteJson";

const wchar_t ADVANCED_PASTE_ADDITIONAL_ACTION_MESSAGE[] = L"AdditionalAction";

const wchar_t ADVANCED_PASTE_CUSTOM_ACTION_MESSAGE[] = L"CustomAction";

// Path to the event used to show Color Picker
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using AdvancedPaste.Helpers;
using AdvancedPaste.Models;
using AdvancedPaste.Settings;
using AdvancedPaste.ViewModels;
using ManagedCommon;
Expand All @@ -31,6 +34,13 @@ public partial class App : Application, IDisposable
{
public IHost Host { get; private set; }

private static readonly Dictionary<string, PasteFormats> AdditionalActionIPCKeys =
typeof(PasteFormats).GetFields()
.Where(field => field.IsLiteral)
.Select(field => (Format: (PasteFormats)field.GetRawConstantValue(), field.GetCustomAttribute<PasteFormatMetadataAttribute>().IPCKey))
.Where(field => field.IPCKey != null)
.ToDictionary(field => field.IPCKey, field => field.Format);

private readonly DispatcherQueue _dispatcherQueue = DispatcherQueue.GetForCurrentThread();
private readonly OptionsViewModel viewModel;

Expand Down Expand Up @@ -128,6 +138,10 @@ private void OnNamedPipeMessage(string message)
{
OnAdvancedPasteJsonHotkey();
}
else if (messageType == PowerToys.Interop.Constants.AdvancedPasteAdditionalActionMessage())
{
OnAdvancedPasteAdditionalActionHotkey(messageParts);
}
else if (messageType == PowerToys.Interop.Constants.AdvancedPasteCustomActionMessage())
{
OnAdvancedPasteCustomActionHotkey(messageParts);
Expand Down Expand Up @@ -156,6 +170,27 @@ private void OnAdvancedPasteHotkey()
ShowWindow();
}

private void OnAdvancedPasteAdditionalActionHotkey(string[] messageParts)
{
if (messageParts.Length != 2)
{
Logger.LogWarning("Unexpected additional action message");
}
else
{
if (!AdditionalActionIPCKeys.TryGetValue(messageParts[1], out PasteFormats pasteFormat))
{
Logger.LogWarning($"Unexpected additional action type {messageParts[1]}");
}
else
{
ShowWindow();
viewModel.ReadClipboard();
viewModel.ExecuteAdditionalAction(pasteFormat);
}
}
}

private void OnAdvancedPasteCustomActionHotkey(string[] messageParts)
{
if (messageParts.Length != 2)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@
x:Name="InputTxtBox"
HorizontalAlignment="Stretch"
x:FieldModifier="public"
IsEnabled="{x:Bind ViewModel.IsClipboardDataText, Mode=OneWay}"
IsEnabled="{x:Bind ViewModel.ClipboardHasData, Mode=OneWay}"
KeyDown="InputTxtBox_KeyDown"
PlaceholderText="{x:Bind ViewModel.InputTxtBoxPlaceholderText, Mode=OneWay}"
Style="{StaticResource CustomTextBoxStyle}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.

using System;
using System.Linq;
using AdvancedPaste.Helpers;
using AdvancedPaste.Settings;
using ManagedCommon;
Expand Down Expand Up @@ -30,13 +31,16 @@ public MainWindow()

void UpdateHeight()
{
var trimmedCustomActionCount = Math.Min(_userSettings.CustomActions.Count, 5);
Height = MinHeight = baseHeight + (trimmedCustomActionCount * 40);
double GetHeight(int maxCustomActionCount) =>
baseHeight + (40 * (_userSettings.AdditionalActions.Count + Math.Min(_userSettings.CustomActions.Count, maxCustomActionCount)));

MinHeight = GetHeight(1);
Height = GetHeight(5);
}

UpdateHeight();

_userSettings.CustomActions.CollectionChanged += (_, _) => UpdateHeight();
_userSettings.Changed += (_, _) => UpdateHeight();

AppWindow.SetIcon("Assets/AdvancedPaste/AdvancedPaste.ico");
this.ExtendsContentIntoTitleBar = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,36 +29,49 @@
</Style.Setters>
</Style>
<DataTemplate x:Key="PasteFormatTemplate" x:DataType="local:PasteFormat">
<Grid>
<ToolTipService.ToolTip>
<TextBlock Text="{x:Bind ToolTip}" />
</ToolTipService.ToolTip>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="26" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<FontIcon
Margin="0,0,0,0"
VerticalAlignment="Center"
AutomationProperties.AccessibilityView="Raw"
FontSize="16"
Glyph="{x:Bind IconGlyph}" />
<TextBlock
Grid.Column="1"
VerticalAlignment="Center"
x:Phase="1"
Text="{x:Bind Name}" />
<TextBlock
Grid.Column="2"
Margin="0,0,8,0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{x:Bind ShortcutText, Mode=OneWay}"
Visibility="{x:Bind ShortcutText.Length, Mode=OneWay, Converter={StaticResource countToVisibilityConverter}}" />
</Grid>
<Button
Margin="0"
Padding="5,0,5,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
AllowFocusOnInteraction="False"
BorderThickness="0"
Click="ListView_Button_Click"
IsEnabled="{x:Bind IsEnabled, Mode=OneWay}">
<Grid Opacity="{x:Bind Opacity, Mode=OneWay}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="26" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>

<ToolTipService.ToolTip>
<TextBlock Text="{x:Bind ToolTip, Mode=OneWay}" />
</ToolTipService.ToolTip>
<FontIcon
Margin="0,0,0,0"
VerticalAlignment="Center"
AutomationProperties.AccessibilityView="Raw"
FontSize="16"
Glyph="{x:Bind IconGlyph, Mode=OneWay}" />
<TextBlock
Grid.Column="1"
VerticalAlignment="Center"
x:Phase="1"
Text="{x:Bind Name, Mode=OneWay}" />
<TextBlock
Grid.Column="2"
Margin="0,0,8,0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{x:Bind ShortcutText, Mode=OneWay}"
Visibility="{x:Bind ShortcutText.Length, Mode=OneWay, Converter={StaticResource countToVisibilityConverter}}" />
</Grid>
</Button>
</DataTemplate>
</Page.Resources>
<Page.KeyboardAccelerators>
Expand Down Expand Up @@ -176,14 +189,21 @@
x:Name="PasteOptionsListView"
Grid.Row="0"
VerticalAlignment="Bottom"
IsEnabled="{x:Bind ViewModel.IsClipboardDataText, Mode=OneWay}"
IsItemClickEnabled="True"
ItemClick="ListView_Click"
IsItemClickEnabled="False"
ItemContainerTransitions="{x:Null}"
ItemTemplate="{StaticResource PasteFormatTemplate}"
ItemsSource="{x:Bind ViewModel.StandardPasteFormats, Mode=OneWay}"
SelectionMode="None"
TabIndex="1" />
TabIndex="1">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Padding" Value="0" />
<Setter Property="Margin" Value="0" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
</ListView>

<Rectangle
Grid.Row="1"
Expand All @@ -196,16 +216,23 @@
x:Name="CustomActionsListView"
Grid.Row="2"
VerticalAlignment="Top"
IsEnabled="{x:Bind ViewModel.IsCustomAIEnabled, Mode=OneWay}"
IsItemClickEnabled="True"
ItemClick="ListView_Click"
IsItemClickEnabled="False"
ItemContainerTransitions="{x:Null}"
ItemTemplate="{StaticResource PasteFormatTemplate}"
ItemsSource="{x:Bind ViewModel.CustomActionPasteFormats, Mode=OneWay}"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ScrollViewer.VerticalScrollMode="Auto"
SelectionMode="None"
TabIndex="2" />
TabIndex="2">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Padding" Value="0" />
<Setter Property="Margin" Value="0" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
</ListView>

<Rectangle
Grid.Row="3"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,9 @@ private void ClipboardHistoryItemDeleteButton_Click(object sender, RoutedEventAr
}
}

private void ListView_Click(object sender, ItemClickEventArgs e)
private void ListView_Button_Click(object sender, RoutedEventArgs e)
{
if (e.ClickedItem is PasteFormat format)
if (sender is Button { DataContext: PasteFormat format })
{
ViewModel.ExecutePasteFormat(format);
}
Expand Down
37 changes: 35 additions & 2 deletions src/modules/AdvancedPaste/AdvancedPaste/Helpers/ClipboardHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
// See the LICENSE file in the project root for more information.

using System;
using System.Threading;
using System.Threading.Tasks;
using ManagedCommon;
using Microsoft.UI.Xaml.Media.Imaging;
using Windows.ApplicationModel.DataTransfer;
using Windows.Graphics.Imaging;
using Windows.Storage;
using Windows.Storage.Streams;
using Windows.System;

Expand Down Expand Up @@ -135,5 +135,38 @@ internal static void SendPasteKeyCombination()

Logger.LogInfo("Paste sent");
}

internal static async Task<SoftwareBitmap> GetClipboardImageContentAsync(DataPackageView clipboardData)
{
using var stream = await GetClipboardImageStreamAsync(clipboardData);
if (stream != null)
{
var decoder = await BitmapDecoder.CreateAsync(stream);
return await decoder.GetSoftwareBitmapAsync();
}

return null;
}

private static async Task<IRandomAccessStream> GetClipboardImageStreamAsync(DataPackageView clipboardData)
{
if (clipboardData.Contains(StandardDataFormats.StorageItems))
{
var storageItems = await clipboardData.GetStorageItemsAsync();
var file = storageItems[0] as StorageFile;
if (file != null)
{
return await file.OpenReadAsync();
}
}

if (clipboardData.Contains(StandardDataFormats.Bitmap))
{
var imageStreamReference = await clipboardData.GetBitmapAsync();
return await imageStreamReference.OpenReadAsync();
}

return null;
}
}
}
10 changes: 8 additions & 2 deletions src/modules/AdvancedPaste/AdvancedPaste/Helpers/IUserSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Collections.ObjectModel;
using System;
using System.Collections.Generic;
using AdvancedPaste.Models;
using Microsoft.PowerToys.Settings.UI.Library;

namespace AdvancedPaste.Settings
Expand All @@ -15,6 +17,10 @@ public interface IUserSettings

public bool CloseAfterLosingFocus { get; }

public ObservableCollection<AdvancedPasteCustomAction> CustomActions { get; }
public IReadOnlyList<AdvancedPasteCustomAction> CustomActions { get; }

public IReadOnlyList<PasteFormats> AdditionalActions { get; }

public event EventHandler Changed;
}
}
Loading

1 comment on commit e79d86d

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@check-spelling-bot Report

🔴 Please review

See the 📜action log or 📝 job summary for details.

Unrecognized words (1)

TKey

Previously acknowledged words that are now absent applayout appsfolder systemsettings SYSTEMWOW USEPOSITION USESIZE 🫥
To accept these unrecognized words as correct and remove the previously acknowledged and now absent words, you could run the following commands

... in a clone of the [email protected]:microsoft/PowerToys.git repository
on the dev/ani/advanced-paste-additional-actions branch (ℹ️ how do I use this?):

curl -s -S -L 'https://raw.githubusercontent.com/check-spelling/check-spelling/v0.0.22/apply.pl' |
perl - 'https://github.com/microsoft/PowerToys/actions/runs/10818078217/attempts/1'
Available 📚 dictionaries could cover words (expected and unrecognized) not in the 📘 dictionary

This includes both expected items (1896) from .github/actions/spell-check/expect.txt and unrecognized words (1)

Dictionary Entries Covers Uniquely
cspell:r/src/r.txt 543 1 1
cspell:cpp/src/people.txt 23 1
cspell:cpp/src/ecosystem.txt 51 1

Consider adding them (in .github/workflows/spelling2.yml) for uses: check-spelling/[email protected] in its with:

      with:
        extra_dictionaries:
          cspell:r/src/r.txt
          cspell:cpp/src/people.txt
          cspell:cpp/src/ecosystem.txt

To stop checking additional dictionaries, add (in .github/workflows/spelling2.yml) for uses: check-spelling/[email protected] in its with:

check_extra_dictionaries: ''
Warnings (1)

See the 📜action log or 📝 job summary for details.

ℹ️ Warnings Count
ℹ️ non-alpha-in-dictionary 1

See ℹ️ Event descriptions for more information.

If the flagged items are 🤯 false positives

If items relate to a ...

  • binary file (or some other file you wouldn't want to check at all).

    Please add a file path to the excludes.txt file matching the containing file.

    File paths are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your files.

    ^ refers to the file's path from the root of the repository, so ^README\.md$ would exclude README.md (on whichever branch you're using).

  • well-formed pattern.

    If you can write a pattern that would match it,
    try adding it to the patterns.txt file.

    Patterns are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your lines.

    Note that patterns can't match multiline strings.

Please sign in to comment.