Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 131 additions & 0 deletions src/Notepads/Controls/TextEditor/TextEditorContextFlyout.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ public sealed class TextEditorContextFlyout : MenuFlyout
private MenuFlyoutItem _previewToggle;
private MenuFlyoutItem _share;

private MenuFlyoutSubItem _capitalizations;
private MenuFlyoutItem _uppercase;
private MenuFlyoutItem _lowercase;
private MenuFlyoutItem _sentencecase;
private MenuFlyoutItem _togglecase;
private MenuFlyoutItem _titlecase;

private MenuFlyout _proofingFlyout;
private readonly MenuFlyoutSeparator _proofingSeparator = new MenuFlyoutSeparator();

Expand All @@ -54,6 +61,7 @@ public TextEditorContextFlyout(ITextEditor editor, TextEditorCore editorCore)
Items.Add(WebSearch);
Items.Add(PreviewToggle);
Items.Add(Share);
Items.Add(Capitalizations);

Opening += TextEditorContextFlyout_Opening;
Closed += TextEditorContextFlyout_Closed;
Expand Down Expand Up @@ -99,6 +107,10 @@ _textEditorCore.ProofingMenuFlyout is MenuFlyout proofingFlyout &&
{
Share.Visibility = Visibility.Collapsed;
}

_textEditorCore.GetTextSelectionPosition(out var start, out var end);
SentenceCase.IsEnabled = start == end;
TitleCase.IsEnabled = start == end;
}

private void BuildProofingSubItems(MenuFlyout proofingFlyout)
Expand Down Expand Up @@ -283,6 +295,125 @@ public MenuFlyoutItem RightToLeftReadingOrder
}
}

public MenuFlyoutItem UpperCase
{
get
{
if (_uppercase != null) return _uppercase;

_uppercase = new MenuFlyoutItem
{
Text = _resourceLoader.GetString("TextEditor_ContextFlyout_UpperCaseButtonDisplayText"),
};
_uppercase.Click += (sender, args) =>
{
_textEditorCore.GetTextSelectionPosition(out var startPos, out var endPos);
_textEditorCore.Capitalize();
_textEditorCore.SetTextSelectionPosition(startPos, endPos);
};
return _uppercase;
}
}

public MenuFlyoutItem LowerCase
{
get
{
if (_lowercase != null) return _lowercase;

_lowercase = new MenuFlyoutItem
{
Text = _resourceLoader.GetString("TextEditor_ContextFlyout_LowerCaseButtonDisplayText"),
};
_lowercase.Click += (sender, args) =>
{
_textEditorCore.GetTextSelectionPosition(out var startPos, out var endPos);
_textEditorCore.Decapitalize();
_textEditorCore.SetTextSelectionPosition(startPos, endPos);
};
return _lowercase;
}
}

public MenuFlyoutItem SentenceCase
{
get
{
if (_sentencecase != null) return _sentencecase;
_sentencecase = new MenuFlyoutItem
{
Text = _resourceLoader.GetString("TextEditor_ContextFlyout_SentenceCaseButtonDisplayText"),
};
_sentencecase.Click += (sender, args) =>
{
_textEditorCore.GetTextSelectionPosition(out var startPos, out var endPos);
_textEditorCore.SentenceCase();
_textEditorCore.SetTextSelectionPosition(startPos, endPos);
};
return _sentencecase;
}
}

public MenuFlyoutItem ToggleCase
{
get
{
if (_togglecase != null) return _togglecase;
_togglecase = new MenuFlyoutItem
{
Text = _resourceLoader.GetString("TextEditor_ContextFlyout_ToggleCaseButtonDisplayText"),
};
_togglecase.Click += (sender, args) =>
{
_textEditorCore.GetTextSelectionPosition(out var startPos, out var endPos);
_textEditorCore.ToggleCase();
_textEditorCore.SetTextSelectionPosition(startPos, endPos);
};
return _togglecase;
}
}

public MenuFlyoutItem TitleCase
{
get
{
if (_titlecase != null) return _titlecase;
_titlecase = new MenuFlyoutItem
{
Text = _resourceLoader.GetString("TextEditor_ContextFlyout_TitleCaseButtonDisplayText"),
};
_titlecase.Click += (sender, args) =>
{
_textEditorCore.GetTextSelectionPosition(out var startPos, out var endPos);
_textEditorCore.TitleCase();
_textEditorCore.SetTextSelectionPosition(startPos, endPos);
};
return _titlecase;
}
}

public MenuFlyoutSubItem Capitalizations
{
get
{
if (_capitalizations != null) return _capitalizations;

_capitalizations = new MenuFlyoutSubItem
{
Text = _resourceLoader.GetString("TextEditor_ContextFlyout_CapitalizationsButtonDisplayText"),
};

_capitalizations.Items.Add(UpperCase);
_capitalizations.Items.Add(LowerCase);
_capitalizations.Items.Add(ToggleCase);
_capitalizations.Items.Add(new MenuFlyoutSeparator());
_capitalizations.Items.Add(TitleCase);
_capitalizations.Items.Add(SentenceCase);

return _capitalizations;
}
}

public MenuFlyoutItem WebSearch
{
get
Expand Down
204 changes: 204 additions & 0 deletions src/Notepads/Controls/TextEditor/TextEditorCore.Capitalizations.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
// ---------------------------------------------------------------------------------------------
// Copyright (c) 2019-2024, Jiaqi (0x7c13) Liu. All rights reserved.
// See LICENSE file in the project root for license information.
// ---------------------------------------------------------------------------------------------

namespace Notepads.Controls.TextEditor
{
using Windows.UI.Text;
using System.Text;

public partial class TextEditorCore
{
public void Capitalize()
{
if (Document.Selection.Length != 0)
{
Document.Selection.ChangeCase(LetterCase.Upper);
}
else
{
Document.GetText(TextGetOptions.None, out var text);
Document.SetText(TextSetOptions.None, text.TrimEnd().ToUpperInvariant());
}
}

public void Decapitalize()
{
if (Document.Selection.Length != 0)
{
Document.Selection.ChangeCase(LetterCase.Lower);
}
else
{
Document.GetText(TextGetOptions.None, out var text);
Document.SetText(TextSetOptions.None, text.TrimEnd().ToLowerInvariant());
}
}

public void SentenceCase()
{
if (Document.Selection.Length != 0)
{
return;
}

Document.GetText(TextGetOptions.None, out var text);

if (string.IsNullOrEmpty(text))
{
return;
}

var sb = new StringBuilder(text);
bool capitalizeNext = true;

for (int i = 0; i < sb.Length; i++)
{
var ch = sb[i];

if (capitalizeNext)
{
if (char.IsWhiteSpace(ch) || IsOpeningPunctuation(ch))
{
continue;
}

if (char.IsLetter(ch))
{
sb[i] = char.ToUpperInvariant(ch);
}
capitalizeNext = false;
}
else
{
if (IsSentenceEndingPunctuation(ch))
{
capitalizeNext = true;
}
}
}

Document.SetText(TextSetOptions.None, sb.ToString().TrimEnd());
}

private static bool IsSentenceEndingPunctuation(char c)
{
return c == '.' || c == '!' || c == '?';
}

private static bool IsOpeningPunctuation(char c)
{
switch (c)
{
case '"':
case '\'':
case '(':
case '[':
case '<':
case '{':
case '\u2018': // ‘
case '\u2019': // ’
case '\u201C': // “
case '\u201D': // ”
case '\u00AB': // «
case '\u00BB': // »
return true;

default:
return false;
}
}

public void TitleCase()
{
if (Document.Selection.Length != 0)
{
Document.Selection.GetText(TextGetOptions.None, out var selectedText);
var output = ToTitleCase(selectedText);
Document.Selection.SetText(TextSetOptions.None, output);
}
else
{
Document.GetText(TextGetOptions.None, out var text);
var output = ToTitleCase(text);
Document.SetText(TextSetOptions.None, output.TrimEnd());
}
}

private string ToTitleCase(string input)
{
if (string.IsNullOrEmpty(input))
{
return string.Empty;
}

var sb = new StringBuilder(input.Length);
bool newWord = true;

foreach (var ch in input)
{
if (char.IsLetter(ch))
{
if (newWord)
{
sb.Append(char.ToUpperInvariant(ch));
}
else
{
sb.Append(char.ToLowerInvariant(ch));
}
newWord = false;
}
else
{
sb.Append(ch);
newWord = true;
}
}

return sb.ToString();
}

public void ToggleCase()
{
if (Document.Selection.Length != 0)
{
Document.Selection.GetText(TextGetOptions.None, out var selectedText);
var output = ToggleCase(selectedText);
Document.Selection.SetText(TextSetOptions.None, output);
}
else
{
Document.GetText(TextGetOptions.None, out var text);
var output = ToggleCase(text);
Document.SetText(TextSetOptions.None, output.TrimEnd());
}
}

private string ToggleCase(string text)
{
if (string.IsNullOrEmpty(text))
{
return string.Empty;
}
var sb = new StringBuilder(text?.Length ?? 0);
foreach (var ch in text)
{
if (char.IsUpper(ch))
{
sb.Append(char.ToLowerInvariant(ch));
}
else if (char.IsLower(ch))
{
sb.Append(char.ToUpperInvariant(ch));
}
else
{
sb.Append(ch);
}
}
return sb.ToString();
}
}
}
1 change: 1 addition & 0 deletions src/Notepads/Notepads.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@
<Compile Include="Controls\Print\PrintPageFormat.xaml.cs">
<DependentUpon>PrintPageFormat.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\TextEditor\TextEditorCore.Capitalizations.cs" />
<Compile Include="Controls\TextEditor\TextEditorCore.JoinText.cs" />
<Compile Include="Controls\TextEditor\TextEditorCore.ExternalEventListener.cs" />
<Compile Include="Controls\TextEditor\TextEditorCore.LineHighlighter.cs" />
Expand Down
Loading