mirror of
https://github.com/Blinue/Magpie.git
synced 2026-06-24 02:04:10 +00:00
修复特定情况下打开文本框右键菜单会崩溃的问题 (#1152)
* feat: 新建配置时可以填入窗口标题 * feat: 添加 TextMenuFlyout * fix: 修复 WinUI 右键菜单导致崩溃 * feat: 右键菜单本地化和代码优化 * feat: 实现撤销和重做 * feat: 扩展新建配置弹窗名称文本框的右键菜单 * chore: 修复编译 * feat: 删除无关功能
This commit is contained in:
parent
b5b2c3859f
commit
0df390c888
47 changed files with 545 additions and 208 deletions
|
|
@ -21,8 +21,4 @@ private:
|
|||
|
||||
}
|
||||
|
||||
namespace winrt::Magpie::factory_implementation {
|
||||
|
||||
struct AboutPage : AboutPageT<AboutPage, implementation::AboutPage> {};
|
||||
|
||||
}
|
||||
BASIC_FACTORY(AboutPage)
|
||||
|
|
|
|||
|
|
@ -114,8 +114,4 @@ private:
|
|||
|
||||
}
|
||||
|
||||
namespace winrt::Magpie::factory_implementation {
|
||||
|
||||
struct App : AppT<App, implementation::App> {};
|
||||
|
||||
}
|
||||
BASIC_FACTORY(App)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ namespace Magpie {
|
|||
#include "IsEqualStateTrigger.idl"
|
||||
#include "IsNullStateTrigger.idl"
|
||||
#include "TextBlockHelper.idl"
|
||||
#include "TextMenuFlyout.idl"
|
||||
#include "SimpleStackPanel.idl"
|
||||
#include "WrapPanel.idl"
|
||||
#include "CaptionButtonsControl.idl"
|
||||
|
|
|
|||
|
|
@ -5,6 +5,4 @@ namespace winrt::Magpie::implementation {
|
|||
struct BlueInfoBarStyle : BlueInfoBarStyleT<BlueInfoBarStyle> {};
|
||||
}
|
||||
|
||||
namespace winrt::Magpie::factory_implementation {
|
||||
struct BlueInfoBarStyle : BlueInfoBarStyleT<BlueInfoBarStyle, implementation::BlueInfoBarStyle> {};
|
||||
}
|
||||
BASIC_FACTORY(BlueInfoBarStyle)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#pragma once
|
||||
#pragma once
|
||||
#include "BoolNegationConverter.g.h"
|
||||
|
||||
namespace winrt::Magpie::implementation {
|
||||
|
|
@ -10,9 +10,4 @@ struct BoolNegationConverter : BoolNegationConverterT<BoolNegationConverter> {
|
|||
|
||||
}
|
||||
|
||||
namespace winrt::Magpie::factory_implementation {
|
||||
|
||||
struct BoolNegationConverter : BoolNegationConverterT<BoolNegationConverter, implementation::BoolNegationConverter> {
|
||||
};
|
||||
|
||||
}
|
||||
BASIC_FACTORY(BoolNegationConverter)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#pragma once
|
||||
#pragma once
|
||||
#include "BoolToNegativeVisibilityConverter.g.h"
|
||||
|
||||
namespace winrt::Magpie::implementation {
|
||||
|
|
@ -10,9 +10,4 @@ struct BoolToNegativeVisibilityConverter : BoolToNegativeVisibilityConverterT<Bo
|
|||
|
||||
}
|
||||
|
||||
namespace winrt::Magpie::factory_implementation {
|
||||
|
||||
struct BoolToNegativeVisibilityConverter : BoolToNegativeVisibilityConverterT<BoolToNegativeVisibilityConverter, implementation::BoolToNegativeVisibilityConverter> {
|
||||
};
|
||||
|
||||
}
|
||||
BASIC_FACTORY(BoolToNegativeVisibilityConverter)
|
||||
|
|
|
|||
|
|
@ -35,9 +35,4 @@ private:
|
|||
|
||||
}
|
||||
|
||||
namespace winrt::Magpie::factory_implementation {
|
||||
|
||||
struct CaptionButtonsControl : CaptionButtonsControlT<CaptionButtonsControl, implementation::CaptionButtonsControl> {
|
||||
};
|
||||
|
||||
}
|
||||
BASIC_FACTORY(CaptionButtonsControl)
|
||||
|
|
|
|||
|
|
@ -1,36 +0,0 @@
|
|||
#pragma once
|
||||
#include "XamlHelper.h"
|
||||
|
||||
namespace Magpie {
|
||||
|
||||
struct ComboBoxHelper {
|
||||
// 用于修复 ComboBox 中存在的问题
|
||||
// 因为官方毫无作为,我不得不使用这些 hack
|
||||
template<typename T>
|
||||
static void DropDownOpened(T const& page, winrt::IInspectable const& sender) {
|
||||
using namespace winrt;
|
||||
using namespace Windows::UI::Xaml::Controls;
|
||||
|
||||
// 修复下拉框不适配主题的问题
|
||||
// https://github.com/microsoft/microsoft-ui-xaml/issues/6622
|
||||
XamlHelper::UpdateThemeOfXamlPopups(page.XamlRoot(), page.ActualTheme());
|
||||
|
||||
// 修复下拉框位置不正确的问题
|
||||
// https://github.com/microsoft/microsoft-ui-xaml/issues/4551
|
||||
ComboBox comboBox = sender.as<ComboBox>();
|
||||
IInspectable selectedItem = comboBox.SelectedItem();
|
||||
if (!selectedItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (std::optional<hstring> str = selectedItem.try_as<hstring>()) {
|
||||
comboBox.PlaceholderText(*str);
|
||||
} else if (ContentControl container = selectedItem.try_as<ContentControl>()) {
|
||||
if (std::optional<hstring> strContent = container.Content().try_as<hstring>()) {
|
||||
comboBox.PlaceholderText(*strContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
46
src/Magpie/ControlHelper.cpp
Normal file
46
src/Magpie/ControlHelper.cpp
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
#include "pch.h"
|
||||
#include "ControlHelper.h"
|
||||
#include "App.h"
|
||||
#include "RootPage.h"
|
||||
|
||||
using namespace winrt;
|
||||
using namespace winrt::Magpie::implementation;
|
||||
using namespace Windows::UI::Xaml::Controls;
|
||||
|
||||
namespace Magpie {
|
||||
|
||||
void ControlHelper::ComboBox_DropDownOpened(const IInspectable& sender) {
|
||||
// 修复下拉框不适配主题的问题
|
||||
// https://github.com/microsoft/microsoft-ui-xaml/issues/6622
|
||||
const auto& rootPage = App::Get().RootPage();
|
||||
XamlHelper::UpdateThemeOfXamlPopups(rootPage->XamlRoot(), rootPage->ActualTheme());
|
||||
|
||||
// 修复下拉框位置不正确的问题
|
||||
// https://github.com/microsoft/microsoft-ui-xaml/issues/4551
|
||||
ComboBox comboBox = sender.as<ComboBox>();
|
||||
IInspectable selectedItem = comboBox.SelectedItem();
|
||||
if (!selectedItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (std::optional<hstring> str = selectedItem.try_as<hstring>()) {
|
||||
comboBox.PlaceholderText(*str);
|
||||
} else if (ContentControl container = selectedItem.try_as<ContentControl>()) {
|
||||
if (std::optional<hstring> strContent = container.Content().try_as<hstring>()) {
|
||||
comboBox.PlaceholderText(*strContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ControlHelper::NumberBox_Loaded(const IInspectable& sender) {
|
||||
// 确保模板已应用
|
||||
sender.as<MUXC::NumberBox>().ApplyTemplate();
|
||||
|
||||
// 设置内部 TextBox 的右键菜单
|
||||
sender.as<IControlProtected>()
|
||||
.GetTemplateChild(L"InputBox")
|
||||
.as<TextBox>()
|
||||
.ContextFlyout(winrt::Magpie::TextMenuFlyout());
|
||||
}
|
||||
|
||||
}
|
||||
15
src/Magpie/ControlHelper.h
Normal file
15
src/Magpie/ControlHelper.h
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
#include "XamlHelper.h"
|
||||
|
||||
namespace Magpie {
|
||||
|
||||
// 用于修复 WinUI 控件存在的问题。因为官方毫无作为,我不得不使用这些 hack
|
||||
struct ControlHelper {
|
||||
// 修复 ComboBox 下拉框的主题和位置
|
||||
static void ComboBox_DropDownOpened(const winrt::IInspectable& sender);
|
||||
|
||||
// 设置 NumberBox 内部 TextBox 的右键菜单
|
||||
static void NumberBox_Loaded(const winrt::IInspectable& sender);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -48,9 +48,4 @@ private:
|
|||
|
||||
}
|
||||
|
||||
namespace winrt::Magpie::factory_implementation {
|
||||
|
||||
struct ControlSizeTrigger : ControlSizeTriggerT<ControlSizeTrigger, implementation::ControlSizeTrigger> {
|
||||
};
|
||||
|
||||
}
|
||||
BASIC_FACTORY(ControlSizeTrigger)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
#include "HomePage.g.cpp"
|
||||
#endif
|
||||
#include "XamlHelper.h"
|
||||
#include "ComboBoxHelper.h"
|
||||
#include "ControlHelper.h"
|
||||
#include "App.h"
|
||||
|
||||
using namespace ::Magpie;
|
||||
|
|
@ -17,7 +17,7 @@ void HomePage::TimerSlider_Loaded(IInspectable const& sender, RoutedEventArgs co
|
|||
}
|
||||
|
||||
void HomePage::ComboBox_DropDownOpened(IInspectable const& sender, IInspectable const&) const {
|
||||
ComboBoxHelper::DropDownOpened(*this, sender);
|
||||
ControlHelper::ComboBox_DropDownOpened(sender);
|
||||
}
|
||||
|
||||
void HomePage::SimulateExclusiveFullscreenToggleSwitch_Toggled(IInspectable const& sender, RoutedEventArgs const&) {
|
||||
|
|
|
|||
|
|
@ -21,8 +21,4 @@ private:
|
|||
|
||||
}
|
||||
|
||||
namespace winrt::Magpie::factory_implementation {
|
||||
|
||||
struct HomePage : HomePageT<HomePage, implementation::HomePage> {};
|
||||
|
||||
}
|
||||
BASIC_FACTORY(HomePage)
|
||||
|
|
|
|||
|
|
@ -123,8 +123,4 @@ private:
|
|||
|
||||
}
|
||||
|
||||
namespace winrt::Magpie::factory_implementation {
|
||||
|
||||
struct HomeViewModel : HomeViewModelT<HomeViewModel, implementation::HomeViewModel> {};
|
||||
|
||||
}
|
||||
BASIC_FACTORY(HomeViewModel)
|
||||
|
|
|
|||
|
|
@ -25,9 +25,4 @@ private:
|
|||
|
||||
}
|
||||
|
||||
namespace winrt::Magpie::factory_implementation {
|
||||
|
||||
struct IsEqualStateTrigger : IsEqualStateTriggerT<IsEqualStateTrigger, implementation::IsEqualStateTrigger> {
|
||||
};
|
||||
|
||||
}
|
||||
BASIC_FACTORY(IsEqualStateTrigger)
|
||||
|
|
|
|||
|
|
@ -22,9 +22,4 @@ private:
|
|||
|
||||
}
|
||||
|
||||
namespace winrt::Magpie::factory_implementation {
|
||||
|
||||
struct IsNullStateTrigger : IsNullStateTriggerT<IsNullStateTrigger, implementation::IsNullStateTrigger> {
|
||||
};
|
||||
|
||||
}
|
||||
BASIC_FACTORY(IsNullStateTrigger)
|
||||
|
|
|
|||
|
|
@ -40,9 +40,5 @@ struct KeyVisualStyle : KeyVisualStyleT<KeyVisualStyle> {};
|
|||
|
||||
}
|
||||
|
||||
namespace winrt::Magpie::factory_implementation {
|
||||
|
||||
struct KeyVisual : KeyVisualT<KeyVisual, implementation::KeyVisual> {};
|
||||
struct KeyVisualStyle : KeyVisualStyleT<KeyVisualStyle, implementation::KeyVisualStyle> {};
|
||||
|
||||
}
|
||||
BASIC_FACTORY(KeyVisual)
|
||||
BASIC_FACTORY(KeyVisualStyle)
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@
|
|||
<DependentUpon>CaptionButtonsControl.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ComboBoxHelper.h" />
|
||||
<ClInclude Include="ControlHelper.h" />
|
||||
<ClInclude Include="ContentDialogHelper.h" />
|
||||
<ClInclude Include="ControlSizeTrigger.h">
|
||||
<DependentUpon>ControlSizeTrigger.idl</DependentUpon>
|
||||
|
|
@ -233,6 +233,10 @@
|
|||
<DependentUpon>TextBlockHelper.idl</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TextMenuFlyout.h">
|
||||
<DependentUpon>TextMenuFlyout.idl</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ThemeHelper.h" />
|
||||
<ClInclude Include="TitleBarControl.h">
|
||||
<DependentUpon>TitleBarControl.xaml</DependentUpon>
|
||||
|
|
@ -291,6 +295,7 @@
|
|||
<SubType>Code</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ContentDialogHelper.cpp" />
|
||||
<ClCompile Include="ControlHelper.cpp" />
|
||||
<ClCompile Include="ControlSizeTrigger.cpp">
|
||||
<DependentUpon>ControlSizeTrigger.idl</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
|
|
@ -421,6 +426,10 @@
|
|||
<DependentUpon>TextBlockHelper.idl</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TextMenuFlyout.cpp">
|
||||
<DependentUpon>TextMenuFlyout.idl</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ThemeHelper.cpp" />
|
||||
<ClCompile Include="TitleBarControl.cpp">
|
||||
<DependentUpon>TitleBarControl.xaml</DependentUpon>
|
||||
|
|
@ -444,6 +453,9 @@
|
|||
<DependentUpon>BlueInfoBarStyle.xaml</DependentUpon>
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
<None Include="TextMenuFlyout.idl">
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
<Midl Include="ToastPage.idl">
|
||||
<DependentUpon>ToastPage.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
|
|
|
|||
|
|
@ -79,15 +79,15 @@
|
|||
<ClCompile Include="SmoothResizeHelper.cpp">
|
||||
<Filter>Helpers</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ControlHelper.cpp">
|
||||
<Filter>Helpers</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="ShortcutService.h">
|
||||
<Filter>Services</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ComboBoxHelper.h">
|
||||
<Filter>Helpers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ShortcutHelper.h">
|
||||
<Filter>Helpers</Filter>
|
||||
</ClInclude>
|
||||
|
|
@ -166,6 +166,9 @@
|
|||
<ClInclude Include="SmoothResizeHelper.h">
|
||||
<Filter>Helpers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ControlHelper.h">
|
||||
<Filter>Helpers</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="Pages">
|
||||
|
|
@ -276,6 +279,9 @@
|
|||
<None Include="SettingsExpanderCornerRadiusConverter.idl">
|
||||
<Filter>Converters</Filter>
|
||||
</None>
|
||||
<None Include="TextMenuFlyout.idl">
|
||||
<Filter>Helpers</Filter>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Include="HomePage.xaml">
|
||||
|
|
|
|||
|
|
@ -38,9 +38,4 @@ private:
|
|||
|
||||
}
|
||||
|
||||
namespace winrt::Magpie::factory_implementation {
|
||||
|
||||
struct PageFrame : PageFrameT<PageFrame, implementation::PageFrame> {
|
||||
};
|
||||
|
||||
}
|
||||
BASIC_FACTORY(PageFrame)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
#include "ProfilePage.g.cpp"
|
||||
#endif
|
||||
#include "Win32Helper.h"
|
||||
#include "ComboBoxHelper.h"
|
||||
#include "ControlHelper.h"
|
||||
#include "ProfileService.h"
|
||||
#include "Profile.h"
|
||||
|
||||
|
|
@ -30,7 +30,11 @@ void ProfilePage::OnNavigatedTo(Navigation::NavigationEventArgs const& args) {
|
|||
}
|
||||
|
||||
void ProfilePage::ComboBox_DropDownOpened(IInspectable const& sender, IInspectable const&) {
|
||||
ComboBoxHelper::DropDownOpened(*this, sender);
|
||||
ControlHelper::ComboBox_DropDownOpened(sender);
|
||||
}
|
||||
|
||||
void ProfilePage::NumberBox_Loaded(IInspectable const& sender, RoutedEventArgs const&) {
|
||||
ControlHelper::NumberBox_Loaded(sender);
|
||||
}
|
||||
|
||||
void ProfilePage::CursorScalingComboBox_SelectionChanged(IInspectable const&, SelectionChangedEventArgs const&) {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ struct ProfilePage : ProfilePageT<ProfilePage> {
|
|||
|
||||
void ComboBox_DropDownOpened(IInspectable const& sender, IInspectable const&);
|
||||
|
||||
void NumberBox_Loaded(IInspectable const& sender, RoutedEventArgs const&);
|
||||
|
||||
void CursorScalingComboBox_SelectionChanged(IInspectable const&, SelectionChangedEventArgs const&);
|
||||
|
||||
void RenameMenuItem_Click(IInspectable const&, RoutedEventArgs const&);
|
||||
|
|
@ -39,8 +41,4 @@ private:
|
|||
|
||||
}
|
||||
|
||||
namespace winrt::Magpie::factory_implementation {
|
||||
|
||||
struct ProfilePage : ProfilePageT<ProfilePage, implementation::ProfilePage> {};
|
||||
|
||||
}
|
||||
BASIC_FACTORY(ProfilePage)
|
||||
|
|
|
|||
|
|
@ -58,7 +58,11 @@
|
|||
Height="32"
|
||||
Margin="0,8,0,20"
|
||||
KeyDown="RenameTextBox_KeyDown"
|
||||
Text="{x:Bind ViewModel.RenameText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
Text="{x:Bind ViewModel.RenameText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
|
||||
<TextBox.ContextFlyout>
|
||||
<local:TextMenuFlyout />
|
||||
</TextBox.ContextFlyout>
|
||||
</TextBox>
|
||||
<Button x:Uid="Profile_MoreOptions_RenameFlyout_OK"
|
||||
MinWidth="80"
|
||||
HorizontalAlignment="Right"
|
||||
|
|
@ -250,6 +254,7 @@
|
|||
Spacing="8">
|
||||
<muxc:NumberBox VerticalAlignment="Center"
|
||||
LargeChange="10"
|
||||
Loaded="NumberBox_Loaded"
|
||||
Maximum="1000"
|
||||
Minimum="10"
|
||||
NumberFormatter="{x:Bind local:App.DoubleFormatter, Mode=OneTime}"
|
||||
|
|
@ -295,6 +300,7 @@
|
|||
Spacing="8">
|
||||
<muxc:NumberBox Foreground="{ThemeResource TextFillColorPrimaryBrush}"
|
||||
LargeChange="10"
|
||||
Loaded="NumberBox_Loaded"
|
||||
Minimum="0"
|
||||
NumberFormatter="{x:Bind local:App.DoubleFormatter, Mode=OneTime}"
|
||||
SmallChange="1"
|
||||
|
|
@ -310,6 +316,7 @@
|
|||
Spacing="8">
|
||||
<muxc:NumberBox Foreground="{ThemeResource TextFillColorPrimaryBrush}"
|
||||
LargeChange="10"
|
||||
Loaded="NumberBox_Loaded"
|
||||
Minimum="0"
|
||||
NumberFormatter="{x:Bind local:App.DoubleFormatter, Mode=OneTime}"
|
||||
SmallChange="1"
|
||||
|
|
@ -325,6 +332,7 @@
|
|||
Spacing="8">
|
||||
<muxc:NumberBox Foreground="{ThemeResource TextFillColorPrimaryBrush}"
|
||||
LargeChange="10"
|
||||
Loaded="NumberBox_Loaded"
|
||||
Minimum="0"
|
||||
NumberFormatter="{x:Bind local:App.DoubleFormatter, Mode=OneTime}"
|
||||
SmallChange="1"
|
||||
|
|
@ -340,6 +348,7 @@
|
|||
Spacing="8">
|
||||
<muxc:NumberBox Foreground="{ThemeResource TextFillColorPrimaryBrush}"
|
||||
LargeChange="10"
|
||||
Loaded="NumberBox_Loaded"
|
||||
Minimum="0"
|
||||
NumberFormatter="{x:Bind local:App.DoubleFormatter, Mode=OneTime}"
|
||||
SmallChange="1"
|
||||
|
|
@ -396,6 +405,7 @@
|
|||
Margin="5,0,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Loaded="NumberBox_Loaded"
|
||||
Maximum="10"
|
||||
Minimum="0.1"
|
||||
NumberFormatter="{x:Bind local:App.DoubleFormatter, Mode=OneTime}"
|
||||
|
|
@ -438,7 +448,11 @@
|
|||
<FontIcon Glyph="" />
|
||||
</local:SettingsCard.HeaderIcon>
|
||||
<TextBox KeyDown="LaunchParametersTextBox_KeyDown"
|
||||
Text="{x:Bind ViewModel.LaunchParameters, Mode=TwoWay}" />
|
||||
Text="{x:Bind ViewModel.LaunchParameters, Mode=TwoWay}">
|
||||
<TextBox.ContextFlyout>
|
||||
<local:TextMenuFlyout />
|
||||
</TextBox.ContextFlyout>
|
||||
</TextBox>
|
||||
</local:SettingsCard>
|
||||
<local:SettingsCard x:Uid="Profile_Advanced_DisableDirectFlip">
|
||||
<ToggleSwitch x:Uid="ToggleSwitch"
|
||||
|
|
|
|||
|
|
@ -877,4 +877,22 @@
|
|||
<data name="Home_Advanced_DeveloperOptions_BenchmarkMode.Content" xml:space="preserve">
|
||||
<value>Benchmark mode</value>
|
||||
</data>
|
||||
<data name="TextMenuFlyout_Copy" xml:space="preserve">
|
||||
<value>Copy</value>
|
||||
</data>
|
||||
<data name="TextMenuFlyout_Cut" xml:space="preserve">
|
||||
<value>Cut</value>
|
||||
</data>
|
||||
<data name="TextMenuFlyout_Paste" xml:space="preserve">
|
||||
<value>Paste</value>
|
||||
</data>
|
||||
<data name="TextMenuFlyout_Redo" xml:space="preserve">
|
||||
<value>Redo</value>
|
||||
</data>
|
||||
<data name="TextMenuFlyout_SelectAll" xml:space="preserve">
|
||||
<value>Select All</value>
|
||||
</data>
|
||||
<data name="TextMenuFlyout_Undo" xml:space="preserve">
|
||||
<value>Undo</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -877,4 +877,22 @@
|
|||
<data name="Home_Advanced_DeveloperOptions_BenchmarkMode.Content" xml:space="preserve">
|
||||
<value>性能测试模式</value>
|
||||
</data>
|
||||
<data name="TextMenuFlyout_Copy" xml:space="preserve">
|
||||
<value>复制</value>
|
||||
</data>
|
||||
<data name="TextMenuFlyout_Cut" xml:space="preserve">
|
||||
<value>剪切</value>
|
||||
</data>
|
||||
<data name="TextMenuFlyout_Paste" xml:space="preserve">
|
||||
<value>粘贴</value>
|
||||
</data>
|
||||
<data name="TextMenuFlyout_Redo" xml:space="preserve">
|
||||
<value>重新操作</value>
|
||||
</data>
|
||||
<data name="TextMenuFlyout_SelectAll" xml:space="preserve">
|
||||
<value>全选</value>
|
||||
</data>
|
||||
<data name="TextMenuFlyout_Undo" xml:space="preserve">
|
||||
<value>取消操作</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -10,13 +10,15 @@
|
|||
#include "ProfileService.h"
|
||||
#include "AppXReader.h"
|
||||
#include "IconHelper.h"
|
||||
#include "ComboBoxHelper.h"
|
||||
#include "ControlHelper.h"
|
||||
#include "ThemeHelper.h"
|
||||
#include "ContentDialogHelper.h"
|
||||
#include "LocalizationService.h"
|
||||
#include "App.h"
|
||||
#include "TitleBarControl.h"
|
||||
#include "MainWindow.h"
|
||||
#include "CandidateWindowItem.h"
|
||||
#include "CommonSharedConstants.h"
|
||||
|
||||
using namespace ::Magpie;
|
||||
using namespace winrt;
|
||||
|
|
@ -225,8 +227,8 @@ void RootPage::NavigationView_ItemInvoked(MUXC::NavigationView const&, MUXC::Nav
|
|||
}
|
||||
}
|
||||
|
||||
void RootPage::ComboBox_DropDownOpened(IInspectable const&, IInspectable const&) const {
|
||||
XamlHelper::UpdateThemeOfXamlPopups(XamlRoot(), ActualTheme());
|
||||
void RootPage::ComboBox_DropDownOpened(IInspectable const& sender, IInspectable const&) const {
|
||||
ControlHelper::ComboBox_DropDownOpened(sender);
|
||||
}
|
||||
|
||||
void RootPage::NewProfileConfirmButton_Click(IInspectable const&, RoutedEventArgs const&) {
|
||||
|
|
@ -348,7 +350,8 @@ void RootPage::_UpdateIcons(bool skipDesktop) {
|
|||
continue;
|
||||
}
|
||||
|
||||
MUXC::NavigationViewItem item = navMenuItems.GetAt(FIRST_PROFILE_ITEM_IDX + i).as<MUXC::NavigationViewItem>();
|
||||
MUXC::NavigationViewItem item = navMenuItems.GetAt(FIRST_PROFILE_ITEM_IDX + i)
|
||||
.as<MUXC::NavigationViewItem>();
|
||||
_LoadIcon(item, profiles[i]);
|
||||
}
|
||||
}
|
||||
|
|
@ -390,4 +393,28 @@ void RootPage::_ProfileService_ProfileReordered(uint32_t profileIdx, bool isMove
|
|||
menuItems.InsertAt(curIdx, otherItem);
|
||||
}
|
||||
|
||||
void RootPage::_UpdateNewProfileNameTextBox(bool fillWithTitle) {
|
||||
int idx = _newProfileViewModel->CandidateWindowIndex();
|
||||
if (idx < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
CandidateWindowItem* selectedItem = get_self<CandidateWindowItem>(
|
||||
_newProfileViewModel->CandidateWindows().GetAt(idx).as<winrt::Magpie::CandidateWindowItem>());
|
||||
hstring text = fillWithTitle ? selectedItem->Title() : selectedItem->DefaultProfileName();
|
||||
|
||||
TextBox textBox = NewProfileNameTextBox();
|
||||
if (textBox.Text() == text) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int size = (int)text.size();
|
||||
// 遗憾的是设置 Text 属性会导致撤销/重做历史丢失
|
||||
textBox.Text(std::move(text));
|
||||
// 修改文本后将光标移到最后
|
||||
textBox.Select(size, 0);
|
||||
// 如果文本太长,这个调用可以使视口移到光标位置
|
||||
textBox.Focus(FocusState::Programmatic);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,6 +58,8 @@ private:
|
|||
|
||||
void _ProfileService_ProfileReordered(uint32_t profileIdx, bool isMoveUp);
|
||||
|
||||
void _UpdateNewProfileNameTextBox(bool fillWithTitle);
|
||||
|
||||
::Magpie::MultithreadEvent<bool>::EventRevoker _appThemeChangedRevoker;
|
||||
::Magpie::Event<uint32_t>::EventRevoker _dpiChangedRevoker;
|
||||
|
||||
|
|
@ -66,6 +68,7 @@ private:
|
|||
::Magpie::Event<uint32_t>::EventRevoker _profileRenamedRevoker;
|
||||
::Magpie::Event<uint32_t>::EventRevoker _profileRemovedRevoker;
|
||||
::Magpie::Event<uint32_t, bool>::EventRevoker _profileMovedRevoker;
|
||||
Primitives::FlyoutBase::Opening_revoker _contextFlyoutOpeningRevoker;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,10 +108,15 @@
|
|||
</ComboBox>
|
||||
<local:SimpleStackPanel Spacing="8">
|
||||
<TextBlock x:Uid="Root_NewProfileFlyout_Name" />
|
||||
<TextBox Height="32"
|
||||
<TextBox x:Name="NewProfileNameTextBox"
|
||||
Height="32"
|
||||
HorizontalAlignment="Stretch"
|
||||
KeyDown="NewProfileNameTextBox_KeyDown"
|
||||
Text="{x:Bind NewProfileViewModel.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
Text="{x:Bind NewProfileViewModel.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
|
||||
<TextBox.ContextFlyout>
|
||||
<local:TextMenuFlyout />
|
||||
</TextBox.ContextFlyout>
|
||||
</TextBox>
|
||||
</local:SimpleStackPanel>
|
||||
<local:SimpleStackPanel Spacing="8">
|
||||
<TextBlock x:Uid="Root_NewProfileFlyout_CopyFrom" />
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
#if __has_include("ScalingModesPage.g.cpp")
|
||||
#include "ScalingModesPage.g.cpp"
|
||||
#endif
|
||||
#include "ComboBoxHelper.h"
|
||||
#include "ControlHelper.h"
|
||||
#include "EffectsService.h"
|
||||
#include <parallel_hashmap/phmap.h>
|
||||
|
||||
|
|
@ -19,7 +19,11 @@ ScalingModesPage::ScalingModesPage() {
|
|||
}
|
||||
|
||||
void ScalingModesPage::ComboBox_DropDownOpened(IInspectable const& sender, IInspectable const&) {
|
||||
ComboBoxHelper::DropDownOpened(*this, sender);
|
||||
ControlHelper::ComboBox_DropDownOpened(sender);
|
||||
}
|
||||
|
||||
void ScalingModesPage::NumberBox_Loaded(IInspectable const& sender, RoutedEventArgs const&) {
|
||||
ControlHelper::NumberBox_Loaded(sender);
|
||||
}
|
||||
|
||||
void ScalingModesPage::EffectSettingsCard_Loaded(IInspectable const& sender, RoutedEventArgs const&) {
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ struct ScalingModesPage : ScalingModesPageT<ScalingModesPage> {
|
|||
|
||||
void ComboBox_DropDownOpened(IInspectable const& sender, IInspectable const&);
|
||||
|
||||
void NumberBox_Loaded(IInspectable const& sender, RoutedEventArgs const&);
|
||||
|
||||
void EffectSettingsCard_Loaded(IInspectable const& sender, RoutedEventArgs const&);
|
||||
|
||||
void AddEffectButton_Click(IInspectable const& sender, RoutedEventArgs const&);
|
||||
|
|
@ -41,8 +43,4 @@ private:
|
|||
|
||||
}
|
||||
|
||||
namespace winrt::Magpie::factory_implementation {
|
||||
|
||||
struct ScalingModesPage : ScalingModesPageT<ScalingModesPage, implementation::ScalingModesPage> {};
|
||||
|
||||
}
|
||||
BASIC_FACTORY(ScalingModesPage)
|
||||
|
|
|
|||
|
|
@ -157,7 +157,11 @@
|
|||
Margin="0,8,0,20"
|
||||
KeyDown="{x:Bind RenameTextBox_KeyDown}"
|
||||
SelectionStart="{x:Bind RenameTextBoxSelectionStart, Mode=OneWay}"
|
||||
Text="{x:Bind RenameText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
Text="{x:Bind RenameText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
|
||||
<TextBox.ContextFlyout>
|
||||
<local:TextMenuFlyout />
|
||||
</TextBox.ContextFlyout>
|
||||
</TextBox>
|
||||
<Button x:Uid="ScalingModes_RenameFlyout_OK"
|
||||
MinWidth="80"
|
||||
HorizontalAlignment="Right"
|
||||
|
|
@ -372,12 +376,14 @@
|
|||
</local:SimpleStackPanel.Resources>
|
||||
<local:SimpleStackPanel Spacing="8">
|
||||
<TextBlock x:Uid="ScalingModes_ScaleFlyout_WidthFactor" />
|
||||
<muxc:NumberBox NumberFormatter="{x:Bind local:App.DoubleFormatter, Mode=OneTime}"
|
||||
<muxc:NumberBox Loaded="NumberBox_Loaded"
|
||||
NumberFormatter="{x:Bind local:App.DoubleFormatter, Mode=OneTime}"
|
||||
Value="{x:Bind ScalingFactorX, Mode=TwoWay}" />
|
||||
</local:SimpleStackPanel>
|
||||
<local:SimpleStackPanel Spacing="8">
|
||||
<TextBlock x:Uid="ScalingModes_ScaleFlyout_HeightFactor" />
|
||||
<muxc:NumberBox NumberFormatter="{x:Bind local:App.DoubleFormatter, Mode=OneTime}"
|
||||
<muxc:NumberBox Loaded="NumberBox_Loaded"
|
||||
NumberFormatter="{x:Bind local:App.DoubleFormatter, Mode=OneTime}"
|
||||
Value="{x:Bind ScalingFactorY, Mode=TwoWay}" />
|
||||
</local:SimpleStackPanel>
|
||||
</local:SimpleStackPanel>
|
||||
|
|
@ -394,12 +400,14 @@
|
|||
</local:SimpleStackPanel.Resources>
|
||||
<local:SimpleStackPanel Spacing="8">
|
||||
<TextBlock x:Uid="ScalingModes_ScaleFlyout_WidthPixels" />
|
||||
<muxc:NumberBox NumberFormatter="{x:Bind local:App.DoubleFormatter, Mode=OneTime}"
|
||||
<muxc:NumberBox Loaded="NumberBox_Loaded"
|
||||
NumberFormatter="{x:Bind local:App.DoubleFormatter, Mode=OneTime}"
|
||||
Value="{x:Bind ScalingPixelsX, Mode=TwoWay}" />
|
||||
</local:SimpleStackPanel>
|
||||
<local:SimpleStackPanel Spacing="8">
|
||||
<TextBlock x:Uid="ScalingModes_ScaleFlyout_HeightPixels" />
|
||||
<muxc:NumberBox NumberFormatter="{x:Bind local:App.DoubleFormatter, Mode=OneTime}"
|
||||
<muxc:NumberBox Loaded="NumberBox_Loaded"
|
||||
NumberFormatter="{x:Bind local:App.DoubleFormatter, Mode=OneTime}"
|
||||
Value="{x:Bind ScalingPixelsY, Mode=TwoWay}" />
|
||||
</local:SimpleStackPanel>
|
||||
</local:SimpleStackPanel>
|
||||
|
|
@ -541,7 +549,11 @@
|
|||
<TextBlock x:Uid="ScalingModes_NewScalingModeFlyout_Name" />
|
||||
<TextBox HorizontalAlignment="Stretch"
|
||||
KeyDown="NewScalingModeNameTextBox_KeyDown"
|
||||
Text="{x:Bind ViewModel.NewScalingModeName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
Text="{x:Bind ViewModel.NewScalingModeName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
|
||||
<TextBox.ContextFlyout>
|
||||
<local:TextMenuFlyout />
|
||||
</TextBox.ContextFlyout>
|
||||
</TextBox>
|
||||
</local:SimpleStackPanel>
|
||||
<local:SimpleStackPanel Spacing="8">
|
||||
<TextBlock x:Uid="ScalingModes_NewScalingModeFlyout_CopyFrom" />
|
||||
|
|
|
|||
|
|
@ -102,9 +102,5 @@ struct SettingsCardStyle : SettingsCardStyleT<SettingsCardStyle> {};
|
|||
|
||||
}
|
||||
|
||||
namespace winrt::Magpie::factory_implementation {
|
||||
|
||||
struct SettingsCard : SettingsCardT<SettingsCard, implementation::SettingsCard> {};
|
||||
struct SettingsCardStyle : SettingsCardStyleT<SettingsCardStyle, implementation::SettingsCardStyle> {};
|
||||
|
||||
}
|
||||
BASIC_FACTORY(SettingsCard)
|
||||
BASIC_FACTORY(SettingsCardStyle)
|
||||
|
|
|
|||
|
|
@ -81,9 +81,5 @@ struct SettingsExpanderStyle : SettingsExpanderStyleT<SettingsExpanderStyle> {};
|
|||
|
||||
}
|
||||
|
||||
namespace winrt::Magpie::factory_implementation {
|
||||
|
||||
struct SettingsExpander : SettingsExpanderT<SettingsExpander, implementation::SettingsExpander> {};
|
||||
struct SettingsExpanderStyle : SettingsExpanderStyleT<SettingsExpanderStyle, implementation::SettingsExpanderStyle> {};
|
||||
|
||||
}
|
||||
BASIC_FACTORY(SettingsExpander)
|
||||
BASIC_FACTORY(SettingsExpanderStyle)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#pragma once
|
||||
#pragma once
|
||||
#include "SettingsExpanderCornerRadiusConverter.g.h"
|
||||
|
||||
namespace winrt::Magpie::implementation {
|
||||
|
|
@ -10,9 +10,4 @@ struct SettingsExpanderCornerRadiusConverter : SettingsExpanderCornerRadiusConve
|
|||
|
||||
}
|
||||
|
||||
namespace winrt::Magpie::factory_implementation {
|
||||
|
||||
struct SettingsExpanderCornerRadiusConverter : SettingsExpanderCornerRadiusConverterT<SettingsExpanderCornerRadiusConverter, implementation::SettingsExpanderCornerRadiusConverter> {
|
||||
};
|
||||
|
||||
}
|
||||
BASIC_FACTORY(SettingsExpanderCornerRadiusConverter)
|
||||
|
|
|
|||
|
|
@ -32,9 +32,5 @@ struct SettingsGroupStyle : SettingsGroupStyleT<SettingsGroupStyle> {};
|
|||
|
||||
}
|
||||
|
||||
namespace winrt::Magpie::factory_implementation {
|
||||
|
||||
struct SettingsGroup : SettingsGroupT<SettingsGroup, implementation::SettingsGroup> {};
|
||||
struct SettingsGroupStyle : SettingsGroupStyleT<SettingsGroupStyle, implementation::SettingsGroupStyle> {};
|
||||
|
||||
}
|
||||
BASIC_FACTORY(SettingsGroup)
|
||||
BASIC_FACTORY(SettingsGroupStyle)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
#if __has_include("SettingsPage.g.cpp")
|
||||
#include "SettingsPage.g.cpp"
|
||||
#endif
|
||||
#include "ComboBoxHelper.h"
|
||||
#include "ControlHelper.h"
|
||||
|
||||
using namespace ::Magpie;
|
||||
using namespace winrt;
|
||||
|
|
@ -21,7 +21,7 @@ void SettingsPage::InitializeComponent() {
|
|||
}
|
||||
|
||||
void SettingsPage::ComboBox_DropDownOpened(IInspectable const& sender, IInspectable const&) const {
|
||||
ComboBoxHelper::DropDownOpened(*this, sender);
|
||||
ControlHelper::ComboBox_DropDownOpened(sender);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,8 +19,4 @@ private:
|
|||
|
||||
}
|
||||
|
||||
namespace winrt::Magpie::factory_implementation {
|
||||
|
||||
struct SettingsPage : SettingsPageT<SettingsPage, implementation::SettingsPage> {};
|
||||
|
||||
}
|
||||
BASIC_FACTORY(SettingsPage)
|
||||
|
|
|
|||
|
|
@ -53,9 +53,4 @@ private:
|
|||
|
||||
}
|
||||
|
||||
namespace winrt::Magpie::factory_implementation {
|
||||
|
||||
struct ShortcutControl : ShortcutControlT<ShortcutControl, implementation::ShortcutControl> {
|
||||
};
|
||||
|
||||
}
|
||||
BASIC_FACTORY(ShortcutControl)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#pragma once
|
||||
#pragma once
|
||||
#include "SimpleStackPanel.g.h"
|
||||
|
||||
namespace winrt::Magpie::implementation {
|
||||
|
|
@ -27,9 +27,4 @@ private:
|
|||
|
||||
}
|
||||
|
||||
namespace winrt::Magpie::factory_implementation {
|
||||
|
||||
struct SimpleStackPanel : SimpleStackPanelT<SimpleStackPanel, implementation::SimpleStackPanel> {
|
||||
};
|
||||
|
||||
}
|
||||
BASIC_FACTORY(SimpleStackPanel)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
#include "App.h"
|
||||
|
||||
using namespace ::Magpie;
|
||||
using namespace winrt::Magpie::implementation;
|
||||
using namespace winrt;
|
||||
using namespace Windows::UI::Xaml::Controls;
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ namespace winrt::Magpie::implementation {
|
|||
|
||||
// 当 TextBlock 被截断时自动设置 Tooltip
|
||||
// https://stackoverflow.com/questions/21615593/how-can-i-automatically-show-a-tooltip-if-the-text-is-too-long
|
||||
|
||||
struct TextBlockHelper {
|
||||
static void RegisterDependencyProperties();
|
||||
static DependencyProperty IsAutoTooltipProperty() { return _isAutoTooltipProperty; }
|
||||
|
|
@ -28,9 +27,4 @@ private:
|
|||
|
||||
}
|
||||
|
||||
namespace winrt::Magpie::factory_implementation {
|
||||
|
||||
struct TextBlockHelper : TextBlockHelperT<TextBlockHelper, implementation::TextBlockHelper> {
|
||||
};
|
||||
|
||||
}
|
||||
BASIC_FACTORY(TextBlockHelper)
|
||||
|
|
|
|||
238
src/Magpie/TextMenuFlyout.cpp
Normal file
238
src/Magpie/TextMenuFlyout.cpp
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
#include "pch.h"
|
||||
#include "TextMenuFlyout.h"
|
||||
#if __has_include("TextMenuFlyout.g.cpp")
|
||||
#include "TextMenuFlyout.g.cpp"
|
||||
#endif
|
||||
#include "CommonSharedConstants.h"
|
||||
|
||||
using namespace winrt::Windows::UI::Xaml::Input;
|
||||
|
||||
namespace winrt::Magpie::implementation {
|
||||
|
||||
TextMenuFlyout::TextMenuFlyout() {
|
||||
// 大部分初始化推迟到 MenuFlyout_Opening
|
||||
Opening({ this, &TextMenuFlyout::MenuFlyout_Opening });
|
||||
}
|
||||
|
||||
void TextMenuFlyout::MenuFlyout_Opening(IInspectable const&, IInspectable const&) {
|
||||
FrameworkElement target = Target();
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool hasSelection = false;
|
||||
bool hasText = false;
|
||||
bool canUndo = false;
|
||||
bool canRedo = false;
|
||||
// 目前使用的 TextBox 和 NumberBox 都支持修改
|
||||
constexpr bool writable = true;
|
||||
|
||||
{
|
||||
TextBox textBox = target.try_as<TextBox>();
|
||||
if (!textBox) {
|
||||
if (MUXC::NumberBox numberBox = target.try_as<MUXC::NumberBox>()) {
|
||||
textBox = numberBox.as<IControlProtected>()
|
||||
.GetTemplateChild(L"InputBox")
|
||||
.as<TextBox>();
|
||||
}
|
||||
}
|
||||
if (textBox) {
|
||||
hasSelection = !textBox.SelectedText().empty();
|
||||
hasText = !textBox.Text().empty();
|
||||
canUndo = textBox.CanUndo();
|
||||
canRedo = textBox.CanRedo();
|
||||
}
|
||||
}
|
||||
|
||||
// 延迟初始化
|
||||
if (!_copy) {
|
||||
std::vector<MenuFlyoutItemBase> items;
|
||||
|
||||
ResourceLoader resourceLoader =
|
||||
ResourceLoader::GetForCurrentView(CommonSharedConstants::APP_RESOURCE_MAP_ID);
|
||||
|
||||
if (writable) {
|
||||
_cut = items.emplace_back(_CreateMenuItem(
|
||||
Symbol::Cut,
|
||||
resourceLoader.GetString(L"TextMenuFlyout_Cut"),
|
||||
{ this, &TextMenuFlyout::Cut_Click },
|
||||
VirtualKeyModifiers::Control,
|
||||
VirtualKey::X
|
||||
));
|
||||
}
|
||||
_copy = items.emplace_back(_CreateMenuItem(
|
||||
Symbol::Copy,
|
||||
resourceLoader.GetString(L"TextMenuFlyout_Copy"),
|
||||
{ this, &TextMenuFlyout::Copy_Click },
|
||||
VirtualKeyModifiers::Control,
|
||||
VirtualKey::C
|
||||
));
|
||||
if (writable) {
|
||||
items.emplace_back(_CreateMenuItem(
|
||||
Symbol::Paste,
|
||||
resourceLoader.GetString(L"TextMenuFlyout_Paste"),
|
||||
{ this, &TextMenuFlyout::Paste_Click },
|
||||
VirtualKeyModifiers::Control,
|
||||
VirtualKey::V
|
||||
));
|
||||
_undo = _CreateMenuItem(
|
||||
Symbol::Undo,
|
||||
resourceLoader.GetString(L"TextMenuFlyout_Undo"),
|
||||
{ this, &TextMenuFlyout::Undo_Click },
|
||||
VirtualKeyModifiers::Control,
|
||||
VirtualKey::Z
|
||||
);
|
||||
items.emplace_back(_undo);
|
||||
_redo = _CreateMenuItem(
|
||||
Symbol::Redo,
|
||||
resourceLoader.GetString(L"TextMenuFlyout_Redo"),
|
||||
{ this, &TextMenuFlyout::Redo_Click },
|
||||
VirtualKeyModifiers::Control,
|
||||
VirtualKey::Y
|
||||
);
|
||||
items.emplace_back(_redo);
|
||||
}
|
||||
_selectAll = _CreateMenuItem(
|
||||
Symbol{},
|
||||
resourceLoader.GetString(L"TextMenuFlyout_SelectAll"),
|
||||
{ this, &TextMenuFlyout::SelectAll_Click },
|
||||
VirtualKeyModifiers::Control,
|
||||
VirtualKey::A
|
||||
);
|
||||
items.emplace_back(_selectAll);
|
||||
|
||||
Items().ReplaceAll({ items.data(), (uint32_t)items.size() });
|
||||
}
|
||||
|
||||
_copy.Visibility(hasSelection ? Visibility::Visible : Visibility::Collapsed);
|
||||
_selectAll.Visibility(hasText ? Visibility::Visible : Visibility::Collapsed);
|
||||
if (writable) {
|
||||
_cut.Visibility(hasSelection ? Visibility::Visible : Visibility::Collapsed);
|
||||
_undo.Visibility(canUndo ? Visibility::Visible : Visibility::Collapsed);
|
||||
_redo.Visibility(canRedo ? Visibility::Visible : Visibility::Collapsed);
|
||||
}
|
||||
}
|
||||
|
||||
void TextMenuFlyout::Cut_Click(IInspectable const&, RoutedEventArgs const&) {
|
||||
// 右键菜单关闭后仍会接收到文本框上的快捷键事件,可以安全忽略,因为 TextBox
|
||||
// 仍会正常处理,除了 Ctrl+A
|
||||
FrameworkElement target = Target();
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (TextBox textBox = target.try_as<TextBox>()) {
|
||||
textBox.CutSelectionToClipboard();
|
||||
} else if (MUXC::NumberBox numberBox = target.try_as<MUXC::NumberBox>()) {
|
||||
numberBox.as<IControlProtected>().GetTemplateChild(L"InputBox").as<TextBox>().CutSelectionToClipboard();
|
||||
}
|
||||
}
|
||||
|
||||
void TextMenuFlyout::Copy_Click(IInspectable const&, RoutedEventArgs const&) {
|
||||
FrameworkElement target = Target();
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (TextBox textBox = target.try_as<TextBox>()) {
|
||||
textBox.CopySelectionToClipboard();
|
||||
} else if (MUXC::NumberBox numberBox = target.try_as<MUXC::NumberBox>()) {
|
||||
numberBox.as<IControlProtected>()
|
||||
.GetTemplateChild(L"InputBox")
|
||||
.as<TextBox>()
|
||||
.CopySelectionToClipboard();
|
||||
}
|
||||
}
|
||||
|
||||
void TextMenuFlyout::Paste_Click(IInspectable const&, RoutedEventArgs const&) {
|
||||
FrameworkElement target = Target();
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (TextBox textBox = target.try_as<TextBox>()) {
|
||||
textBox.PasteFromClipboard();
|
||||
} else if (MUXC::NumberBox numberBox = target.try_as<MUXC::NumberBox>()) {
|
||||
numberBox.as<IControlProtected>()
|
||||
.GetTemplateChild(L"InputBox")
|
||||
.as<TextBox>()
|
||||
.PasteFromClipboard();
|
||||
}
|
||||
}
|
||||
|
||||
void TextMenuFlyout::Undo_Click(IInspectable const&, RoutedEventArgs const&) {
|
||||
FrameworkElement target = Target();
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (TextBox textBox = target.try_as<TextBox>()) {
|
||||
textBox.Undo();
|
||||
} else if (MUXC::NumberBox numberBox = target.try_as<MUXC::NumberBox>()) {
|
||||
numberBox.as<IControlProtected>()
|
||||
.GetTemplateChild(L"InputBox")
|
||||
.as<TextBox>()
|
||||
.Undo();
|
||||
}
|
||||
}
|
||||
|
||||
void TextMenuFlyout::Redo_Click(IInspectable const&, RoutedEventArgs const&) {
|
||||
FrameworkElement target = Target();
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (TextBox textBox = target.try_as<TextBox>()) {
|
||||
textBox.Redo();
|
||||
} else if (MUXC::NumberBox numberBox = target.try_as<MUXC::NumberBox>()) {
|
||||
numberBox.as<IControlProtected>()
|
||||
.GetTemplateChild(L"InputBox")
|
||||
.as<TextBox>()
|
||||
.Redo();
|
||||
}
|
||||
}
|
||||
|
||||
void TextMenuFlyout::SelectAll_Click(IInspectable const&, RoutedEventArgs const&) {
|
||||
// !!! HACK !!!
|
||||
// 由于 WinUI 的 bug,一旦右键菜单被打开过一次,TextBox 就不再处理 Ctrl+A,而我们
|
||||
// 仍会收到回调。这里必须自己实现全选功能,否则 Ctrl+A 会永久失效。
|
||||
IInspectable target = Target();
|
||||
if (!target) {
|
||||
target = FocusManager::GetFocusedElement(XamlRoot());
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (TextBox textBox = target.try_as<TextBox>()) {
|
||||
textBox.SelectAll();
|
||||
} else if (MUXC::NumberBox numberBox = target.try_as<MUXC::NumberBox>()) {
|
||||
numberBox.as<IControlProtected>()
|
||||
.GetTemplateChild(L"InputBox")
|
||||
.as<TextBox>()
|
||||
.SelectAll();
|
||||
}
|
||||
}
|
||||
|
||||
MenuFlyoutItemBase TextMenuFlyout::_CreateMenuItem(
|
||||
Symbol symbol,
|
||||
hstring text,
|
||||
RoutedEventHandler click,
|
||||
VirtualKeyModifiers modifiers,
|
||||
VirtualKey key
|
||||
) {
|
||||
KeyboardAccelerator accel;
|
||||
accel.Modifiers(modifiers);
|
||||
accel.Key(key);
|
||||
|
||||
MenuFlyoutItem item;
|
||||
if (symbol != Symbol{}) {
|
||||
item.Icon(SymbolIcon{ std::move(symbol) });
|
||||
}
|
||||
item.Text(std::move(text));
|
||||
item.Click(std::move(click));
|
||||
item.KeyboardAccelerators().Append(std::move(accel));
|
||||
return item;
|
||||
}
|
||||
|
||||
}
|
||||
46
src/Magpie/TextMenuFlyout.h
Normal file
46
src/Magpie/TextMenuFlyout.h
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
#pragma once
|
||||
#include "TextMenuFlyout.g.h"
|
||||
|
||||
namespace winrt::Magpie::implementation {
|
||||
|
||||
// GH#1070
|
||||
//
|
||||
// 移植自 https://github.com/microsoft/terminal/pull/18854
|
||||
//
|
||||
// 之所以使用自定义右键菜单,是因为当一个线程中创建了多个 XAML Islands 窗口,默认的
|
||||
// 右键菜单会导致崩溃。应确保覆盖所有右键菜单,目前包括 TextBox 和 MUXC::NumberBox。
|
||||
//
|
||||
// 我还尝试过其他方案(如 MUXC::TextCommandBarFlyout),但都不尽人意。目前看来对每个
|
||||
// TextBox 和 NumberBox 单独设置 TextMenuFlyout 是最稳妥的方案。
|
||||
struct TextMenuFlyout : TextMenuFlyoutT<TextMenuFlyout> {
|
||||
TextMenuFlyout();
|
||||
|
||||
void MenuFlyout_Opening(IInspectable const&, IInspectable const&);
|
||||
void Cut_Click(IInspectable const&, RoutedEventArgs const&);
|
||||
void Copy_Click(IInspectable const&, RoutedEventArgs const&);
|
||||
void Paste_Click(IInspectable const&, RoutedEventArgs const&);
|
||||
void Undo_Click(IInspectable const&, RoutedEventArgs const&);
|
||||
void Redo_Click(IInspectable const&, RoutedEventArgs const&);
|
||||
void SelectAll_Click(IInspectable const&, RoutedEventArgs const&);
|
||||
|
||||
private:
|
||||
MenuFlyoutItemBase _CreateMenuItem(
|
||||
Symbol symbol,
|
||||
hstring text,
|
||||
RoutedEventHandler click,
|
||||
VirtualKeyModifiers modifiers,
|
||||
VirtualKey key
|
||||
);
|
||||
|
||||
// 始终存在的条目
|
||||
MenuFlyoutItemBase _copy{ nullptr };
|
||||
MenuFlyoutItemBase _selectAll{ nullptr };
|
||||
// 只适用于可编辑的控件的条目
|
||||
MenuFlyoutItemBase _cut{ nullptr };
|
||||
MenuFlyoutItemBase _undo{ nullptr };
|
||||
MenuFlyoutItemBase _redo{ nullptr };
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
BASIC_FACTORY(TextMenuFlyout)
|
||||
6
src/Magpie/TextMenuFlyout.idl
Normal file
6
src/Magpie/TextMenuFlyout.idl
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
namespace Magpie {
|
||||
[default_interface]
|
||||
runtimeclass TextMenuFlyout : Windows.UI.Xaml.Controls.MenuFlyout {
|
||||
TextMenuFlyout();
|
||||
}
|
||||
}
|
||||
|
|
@ -30,9 +30,4 @@ private:
|
|||
|
||||
}
|
||||
|
||||
namespace winrt::Magpie::factory_implementation {
|
||||
|
||||
struct TitleBarControl : TitleBarControlT<TitleBarControl, implementation::TitleBarControl> {
|
||||
};
|
||||
|
||||
}
|
||||
BASIC_FACTORY(TitleBarControl)
|
||||
|
|
|
|||
|
|
@ -109,9 +109,4 @@ private:
|
|||
|
||||
}
|
||||
|
||||
namespace winrt::Magpie::factory_implementation {
|
||||
|
||||
struct WrapPanel : WrapPanelT<WrapPanel, implementation::WrapPanel> {
|
||||
};
|
||||
|
||||
}
|
||||
BASIC_FACTORY(WrapPanel)
|
||||
|
|
|
|||
|
|
@ -99,3 +99,10 @@ using namespace std::chrono_literals;
|
|||
// 导入 winrt 命名空间的 co_await 重载
|
||||
// https://devblogs.microsoft.com/oldnewthing/20191219-00/?p=103230
|
||||
using winrt::operator co_await;
|
||||
|
||||
|
||||
// 简化工厂类的创建
|
||||
#define BASIC_FACTORY(className) \
|
||||
namespace winrt::Magpie::factory_implementation { \
|
||||
struct className : className##T<className, implementation::className> {}; \
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue