Skip to content

Commit b7db581

Browse files
authored
Merge pull request #11713 from SubtitleEdit/fix/textbox-italic-smart-whitespace-11711
Make text box italic/bold/underline toggle intelligent like SE4 (#11711)
2 parents 64b75aa + 7ccfa1f commit b7db581

1 file changed

Lines changed: 68 additions & 35 deletions

File tree

src/ui/Features/Main/MainViewModel.cs

Lines changed: 68 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -11360,23 +11360,23 @@ private void TextBoxRemoveAllFormatting()
1136011360
private void TextBoxBold()
1136111361
{
1136211362
var tb = EditTextBox;
11363-
ToggleTextBoxTag(tb, "b", "b1", "b0");
11363+
ToggleTextBoxTag(tb, "b");
1136411364
_updateAudioVisualizer = true;
1136511365
}
1136611366

1136711367
[RelayCommand]
1136811368
private void TextBoxItalic()
1136911369
{
1137011370
var tb = EditTextBox;
11371-
ToggleTextBoxTag(tb, "i", "i1", "i0");
11371+
ToggleTextBoxTag(tb, "i");
1137211372
_updateAudioVisualizer = true;
1137311373
}
1137411374

1137511375
[RelayCommand]
1137611376
private void TextBoxUnderline()
1137711377
{
1137811378
var tb = EditTextBox;
11379-
ToggleTextBoxTag(tb, "u", "u1", "u0");
11379+
ToggleTextBoxTag(tb, "u");
1138011380
_updateAudioVisualizer = true;
1138111381
}
1138211382

@@ -13443,72 +13443,105 @@ public void SelectAndScrollToSubtitle(SubtitleLineViewModel subtitle)
1344313443
}, DispatcherPriority.Background);
1344413444
}
1344513445

13446-
private bool ToggleTextBoxTag(ITextBoxWrapper tb, string htmlTag, string assaOn, string assaOff)
13446+
private bool ToggleTextBoxTag(ITextBoxWrapper tb, string tag)
1344713447
{
1344813448
if (tb == null || tb.Text == null)
1344913449
{
1345013450
return false;
1345113451
}
1345213452

13453+
var isAssa = SelectedSubtitleFormat is AdvancedSubStationAlpha;
1345313454
var selectionStart = Math.Min(tb.SelectionStart, tb.SelectionEnd);
1345413455
var selectionEnd = Math.Max(tb.SelectionStart, tb.SelectionEnd);
1345513456
var selectionLength = selectionEnd - selectionStart;
1345613457

13457-
var isAssa = SelectedSubtitleFormat is AdvancedSubStationAlpha;
13458+
// No text selected - toggle the whole line (or just the current dialog line).
1345813459
if (selectionLength == 0)
1345913460
{
13460-
if (isAssa)
13461+
var text = tb.Text;
13462+
var lines = text.SplitToLines();
13463+
13464+
// Find the line where the caret is currently located (do not count wrapped lines).
13465+
var numberOfNewLines = 0;
13466+
for (var i = 0; i < selectionStart && i < text.Length; i++)
1346113467
{
13462-
if (tb.Text.Contains("{\\" + assaOn + "}"))
13468+
if (text[i] == '\n')
1346313469
{
13464-
tb.Text = tb.Text.Replace("{\\" + assaOn + "}", string.Empty)
13465-
.Replace("{\\" + assaOff + "}", string.Empty);
13466-
}
13467-
else
13468-
{
13469-
tb.Text = "{\\" + assaOn + "}" + tb.Text + "{\\" + assaOff + "}";
13470+
numberOfNewLines++;
1347013471
}
1347113472
}
13473+
13474+
var selectedLineIdx = Math.Min(numberOfNewLines, lines.Count - 1);
13475+
var selectedLine = lines[selectedLineIdx];
13476+
13477+
// When the caret is on a dialog line ("- ..."), only toggle that line so the
13478+
// other speaker's line keeps its formatting.
13479+
var isDialog = selectedLine.StartsWith('-') ||
13480+
selectedLine.StartsWith("<" + tag + ">-", StringComparison.OrdinalIgnoreCase);
13481+
13482+
var textLen = text.Length;
13483+
if (isDialog)
13484+
{
13485+
lines[selectedLineIdx] = HtmlUtil.ToggleTag(selectedLine, tag, false, isAssa);
13486+
text = string.Join(Environment.NewLine, lines);
13487+
}
1347213488
else
1347313489
{
13474-
if (tb.Text.Contains("<" + htmlTag + ">"))
13475-
{
13476-
tb.Text = HtmlUtil.RemoveOpenCloseTags(tb.Text, htmlTag);
13477-
}
13478-
else
13479-
{
13480-
tb.Text = "<" + htmlTag + ">" + tb.Text + "</" + htmlTag + ">";
13481-
}
13490+
text = HtmlUtil.ToggleTag(text, tag, false, isAssa);
1348213491
}
13492+
13493+
tb.Text = text;
13494+
13495+
// Keep the caret next to where it was, shifting by the length of the opening
13496+
// tag inserted before it: "<i>" (3) for HTML, "{\i1}" (5) for ASSA.
13497+
var openTagLength = isAssa ? tag.Length + 4 : tag.Length + 2;
13498+
var newCaret = textLen > text.Length
13499+
? Math.Max(selectionStart - openTagLength, 0)
13500+
: selectionStart + openTagLength;
13501+
Dispatcher.UIThread.Post(() =>
13502+
{
13503+
tb.Focus();
13504+
tb.SelectionStart = newCaret;
13505+
tb.SelectionEnd = newCaret;
13506+
});
1348313507
}
1348413508
else
1348513509
{
13510+
// Move leading/trailing white-space (spaces and new-lines) outside the tag so
13511+
// " 'word'" becomes " <i>'word'</i>" instead of "<i> 'word'</i>".
13512+
var pre = string.Empty;
13513+
var post = string.Empty;
1348613514
var selectedText = tb.Text.Substring(selectionStart, selectionLength);
13487-
13488-
if (isAssa)
13515+
while (selectedText.EndsWith(' ') || selectedText.EndsWith(Environment.NewLine, StringComparison.Ordinal) ||
13516+
selectedText.StartsWith(' ') || selectedText.StartsWith(Environment.NewLine, StringComparison.Ordinal))
1348913517
{
13490-
if (selectedText.Contains("{\\" + assaOn + "}"))
13518+
if (selectedText.EndsWith(' '))
1349113519
{
13492-
selectedText = selectedText.Replace("{\\" + assaOn + "}", string.Empty)
13493-
.Replace("{\\" + assaOff + "}", string.Empty);
13520+
post = " " + post;
13521+
selectedText = selectedText.Remove(selectedText.Length - 1);
1349413522
}
13495-
else
13523+
13524+
if (selectedText.EndsWith(Environment.NewLine, StringComparison.Ordinal))
1349613525
{
13497-
selectedText = "{\\" + assaOn + "}" + selectedText + "{\\" + assaOff + "}";
13526+
post = Environment.NewLine + post;
13527+
selectedText = selectedText.Remove(selectedText.Length - Environment.NewLine.Length);
1349813528
}
13499-
}
13500-
else
13501-
{
13502-
if (selectedText.Contains("<" + htmlTag + ">"))
13529+
13530+
if (selectedText.StartsWith(' '))
1350313531
{
13504-
selectedText = HtmlUtil.RemoveOpenCloseTags(selectedText, htmlTag);
13532+
pre += " ";
13533+
selectedText = selectedText.Remove(0, 1);
1350513534
}
13506-
else
13535+
13536+
if (selectedText.StartsWith(Environment.NewLine, StringComparison.Ordinal))
1350713537
{
13508-
selectedText = "<" + htmlTag + ">" + selectedText + "</" + htmlTag + ">";
13538+
pre += Environment.NewLine;
13539+
selectedText = selectedText.Remove(0, Environment.NewLine.Length);
1350913540
}
1351013541
}
1351113542

13543+
selectedText = pre + HtmlUtil.ToggleTag(selectedText, tag, false, isAssa) + post;
13544+
1351213545
tb.Text = tb.Text
1351313546
.Remove(selectionStart, selectionLength)
1351413547
.Insert(selectionStart, selectedText);

0 commit comments

Comments
 (0)