1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191 |
- #nullable enable
- using System;
- using System.Collections.Generic;
- using System.Reactive.Linq;
- using System.Threading.Tasks;
- using Avalonia.Controls.Presenters;
- using Avalonia.Controls.Primitives;
- using Avalonia.Controls.Templates;
- using Avalonia.Data;
- using Avalonia.Headless;
- using Avalonia.Input;
- using Avalonia.Input.Platform;
- using Avalonia.Input.TextInput;
- using Avalonia.Layout;
- using Avalonia.Media;
- using Avalonia.Platform;
- using Avalonia.UnitTests;
- using Avalonia.VisualTree;
- using Moq;
- using Xunit;
- namespace Avalonia.Controls.UnitTests
- {
- public class TextBoxTests : ScopedTestBase
- {
- [Fact]
- public void Opening_Context_Menu_Does_not_Lose_Selection()
- {
- using (UnitTestApplication.Start(FocusServices))
- {
- var target1 = new TextBox
- {
- Template = CreateTemplate(),
- Text = "1234",
- ContextMenu = new TestContextMenu()
- };
- var target2 = new TextBox
- {
- Template = CreateTemplate(),
- Text = "5678"
- };
-
- var sp = new StackPanel();
- sp.Children.Add(target1);
- sp.Children.Add(target2);
- target1.ApplyTemplate();
- target2.ApplyTemplate();
-
- var root = new TestRoot() { Child = sp };
- target1.SelectionStart = 0;
- target1.SelectionEnd = 3;
-
- target1.Focus();
- Assert.False(target2.IsFocused);
- Assert.True(target1.IsFocused);
- target2.Focus();
-
- Assert.Equal("123", target1.SelectedText);
- }
- }
- [Fact]
- public void TextBox_Should_Lose_Focus_When_Disabled()
- {
- using (UnitTestApplication.Start(FocusServices))
- {
- var target = new TextBox
- {
- Template = CreateTemplate()
- };
- target.ApplyTemplate();
- var root = new TestRoot() { Child = target };
- target.Focus();
- Assert.True(target.IsFocused);
- target.IsEnabled = false;
- Assert.False(target.IsFocused);
- Assert.False(target.IsEnabled);
- }
- }
- [Fact]
- public void Opening_Context_Flyout_Does_not_Lose_Selection()
- {
- using (UnitTestApplication.Start(FocusServices))
- {
- var target1 = new TextBox
- {
- Template = CreateTemplate(),
- Text = "1234",
- ContextFlyout = new MenuFlyout
- {
- Items =
- {
- new MenuItem { Header = "Item 1" },
- new MenuItem {Header = "Item 2" },
- new MenuItem {Header = "Item 3" }
- }
- }
- };
-
- target1.ApplyTemplate();
- var root = new TestRoot() { Child = target1 };
- target1.SelectionStart = 0;
- target1.SelectionEnd = 3;
- target1.Focus();
- Assert.True(target1.IsFocused);
- target1.ContextFlyout.ShowAt(target1);
- Assert.Equal("123", target1.SelectedText);
- }
- }
- [Fact]
- public void DefaultBindingMode_Should_Be_TwoWay()
- {
- Assert.Equal(
- BindingMode.TwoWay,
- TextBox.TextProperty.GetMetadata(typeof(TextBox)).DefaultBindingMode);
- }
- [Fact]
- public void TextBox_Ignore_Word_Move_In_Password_Field()
- {
- using (UnitTestApplication.Start(Services))
- {
- var target = new TextBox
- {
- Template = CreateTemplate(),
- PasswordChar = '*',
- Text = "passw0rd"
- };
- target.ApplyTemplate();
- target.Measure(Size.Infinity);
- target.CaretIndex = 8;
- RaiseKeyEvent(target, Key.Left, KeyModifiers.Control);
- Assert.Equal(7, target.CaretIndex);
- }
- }
- [Fact]
- public void CaretIndex_Can_Moved_To_Position_After_The_End_Of_Text_With_Arrow_Key()
- {
- using (UnitTestApplication.Start(Services))
- {
- var target = new TextBox
- {
- Template = CreateTemplate(),
- Text = "1234"
- };
- target.ApplyTemplate();
-
- target.Measure(Size.Infinity);
-
- target.CaretIndex = 3;
- RaiseKeyEvent(target, Key.Right, 0);
- Assert.Equal(4, target.CaretIndex);
- }
- }
-
- [Fact]
- public void Control_Backspace_Should_Set_Caret_Position_To_The_Start_Of_The_Deletion()
- {
- using (UnitTestApplication.Start(Services))
- {
- var target = new TextBox
- {
- Template = CreateTemplate(),
- Text = "First Second Third",
- SelectionStart = 13,
- SelectionEnd = 13
- };
- target.CaretIndex = 10;
- target.ApplyTemplate();
- // (First Second |Third)
- RaiseKeyEvent(target, Key.Back, KeyModifiers.Control);
- // (First |Third)
-
- Assert.Equal(6, target.CaretIndex);
- }
- }
-
- [Fact]
- public void Control_Backspace_Should_Remove_The_Double_Whitespace_If_Caret_Index_Was_At_The_End_Of_A_Word()
- {
- using (UnitTestApplication.Start(Services))
- {
- var target = new TextBox
- {
- Template = CreateTemplate(),
- Text = "First Second Third",
- SelectionStart = 12,
- SelectionEnd = 12
- };
- target.ApplyTemplate();
-
- // (First Second| Third)
- RaiseKeyEvent(target, Key.Back, KeyModifiers.Control);
- // (First| Third)
- Assert.Equal("First Third", target.Text);
- }
- }
- [Fact]
- public void Control_Backspace_Undo_Should_Return_Caret_Position()
- {
- using (UnitTestApplication.Start(Services))
- {
- var target = new TextBox
- {
- Template = CreateTemplate(),
- Text = "First Second Third",
- SelectionStart = 9,
- SelectionEnd = 9
- };
- target.ApplyTemplate();
-
- // (First Second| Third)
- RaiseKeyEvent(target, Key.Back, KeyModifiers.Control);
- // (First| Third)
-
- target.Undo();
- // (First Second| Third)
- Assert.Equal(9, target.CaretIndex);
- }
- }
- [Fact]
- public void Press_Ctrl_A_Select_All_Text()
- {
- using (UnitTestApplication.Start(Services))
- {
- var target = new TextBox
- {
- Template = CreateTemplate(),
- Text = "1234"
- };
-
- target.ApplyTemplate();
- RaiseKeyEvent(target, Key.A, KeyModifiers.Control);
- Assert.Equal(0, target.SelectionStart);
- Assert.Equal(4, target.SelectionEnd);
- }
- }
- [Fact]
- public void Press_Ctrl_A_Select_All_Null_Text()
- {
- using (UnitTestApplication.Start(Services))
- {
- var target = new TextBox
- {
- Template = CreateTemplate()
- };
- RaiseKeyEvent(target, Key.A, KeyModifiers.Control);
- Assert.Equal(0, target.SelectionStart);
- Assert.Equal(0, target.SelectionEnd);
- }
- }
- [Fact]
- public void Press_Ctrl_Z_Will_Not_Modify_Text()
- {
- using (UnitTestApplication.Start(Services))
- {
- var target = new TextBox
- {
- Template = CreateTemplate(),
- Text = "1234"
- };
- RaiseKeyEvent(target, Key.Z, KeyModifiers.Control);
- Assert.Equal("1234", target.Text);
- }
- }
- [Fact]
- public void Control_Backspace_Should_Remove_The_Word_Before_The_Caret_If_There_Is_No_Selection()
- {
- using (UnitTestApplication.Start(Services))
- {
- TextBox textBox = new TextBox
- {
- Template = CreateTemplate(),
- Text = "First Second Third Fourth",
- SelectionStart = 5,
- SelectionEnd = 5
- };
-
- textBox.ApplyTemplate();
- // (First| Second Third Fourth)
- RaiseKeyEvent(textBox, Key.Back, KeyModifiers.Control);
- Assert.Equal(" Second Third Fourth", textBox.Text);
- // ( Second |Third Fourth)
- textBox.CaretIndex = 8;
- RaiseKeyEvent(textBox, Key.Back, KeyModifiers.Control);
- Assert.Equal(" Third Fourth", textBox.Text);
- // ( Thi|rd Fourth)
- textBox.CaretIndex = 4;
- RaiseKeyEvent(textBox, Key.Back, KeyModifiers.Control);
- Assert.Equal(" rd Fourth", textBox.Text);
- // ( rd F[ou]rth)
- textBox.SelectionStart = 5;
- textBox.SelectionEnd = 7;
- RaiseKeyEvent(textBox, Key.Back, KeyModifiers.Control);
- Assert.Equal(" rd Frth", textBox.Text);
- // ( |rd Frth)
- textBox.CaretIndex = 1;
- RaiseKeyEvent(textBox, Key.Back, KeyModifiers.Control);
- Assert.Equal("rd Frth", textBox.Text);
- }
- }
- [Fact]
- public void Control_Delete_Should_Remove_The_Word_After_The_Caret_If_There_Is_No_Selection()
- {
- using (UnitTestApplication.Start(Services))
- {
- TextBox textBox = new TextBox
- {
- Template = CreateTemplate(),
- Text = "First Second Third Fourth",
- CaretIndex = 19,
- };
-
- textBox.ApplyTemplate();
- // (First Second Third |Fourth)
- RaiseKeyEvent(textBox, Key.Delete, KeyModifiers.Control);
- Assert.Equal("First Second Third ", textBox.Text);
- // (First Second |Third )
- textBox.CaretIndex = 13;
- RaiseKeyEvent(textBox, Key.Delete, KeyModifiers.Control);
- Assert.Equal("First Second ", textBox.Text);
- // (First Sec|ond )
- textBox.CaretIndex = 9;
- RaiseKeyEvent(textBox, Key.Delete, KeyModifiers.Control);
- Assert.Equal("First Sec", textBox.Text);
- // (Fi[rs]t Sec )
- textBox.SelectionStart = 2;
- textBox.SelectionEnd = 4;
- RaiseKeyEvent(textBox, Key.Delete, KeyModifiers.Control);
- Assert.Equal("Fit Sec", textBox.Text);
- // (Fit Sec| )
- textBox.Text += " ";
- textBox.CaretIndex = 7;
- RaiseKeyEvent(textBox, Key.Delete, KeyModifiers.Control);
- Assert.Equal("Fit Sec", textBox.Text);
- }
- }
- [Fact]
- public void Setting_SelectionStart_To_SelectionEnd_Sets_CaretPosition_To_SelectionStart()
- {
- using (UnitTestApplication.Start(Services))
- {
- var textBox = new TextBox
- {
- Text = "0123456789"
- };
- textBox.SelectionStart = 2;
- textBox.SelectionEnd = 2;
-
- Assert.Equal(2, textBox.CaretIndex);
- }
- }
- [Fact]
- public void Setting_Text_Updates_CaretPosition()
- {
- using (UnitTestApplication.Start(Services))
- {
- var target = new TextBox
- {
- Text = "Initial Text",
- CaretIndex = 11
- };
- var invoked = false;
- target.GetObservable(TextBox.TextProperty).Skip(1).Subscribe(_ =>
- {
- // Caret index should be set before Text changed notification, as we don't want
- // to notify with an invalid CaretIndex.
- Assert.Equal(7, target.CaretIndex);
- invoked = true;
- });
- target.Text = "Changed";
- Assert.True(invoked);
- }
- }
- [Fact]
- public void Press_Enter_Does_Not_Accept_Return()
- {
- using (UnitTestApplication.Start(Services))
- {
- var target = new TextBox
- {
- Template = CreateTemplate(),
- AcceptsReturn = false,
- Text = "1234"
- };
-
- target.ApplyTemplate();
- RaiseKeyEvent(target, Key.Enter, 0);
- Assert.Equal("1234", target.Text);
- }
- }
- [Fact]
- public void Press_Enter_Add_Default_Newline()
- {
- using (UnitTestApplication.Start(Services))
- {
- var target = new TextBox
- {
- Template = CreateTemplate(),
- AcceptsReturn = true
- };
-
- target.ApplyTemplate();
- RaiseKeyEvent(target, Key.Enter, 0);
- Assert.Equal(Environment.NewLine, target.Text);
- }
- }
- [Fact]
- public void Press_Enter_Add_Custom_Newline()
- {
- using (UnitTestApplication.Start(Services))
- {
- var target = new TextBox
- {
- Template = CreateTemplate(),
- AcceptsReturn = true,
- NewLine = "Test"
- };
-
- target.ApplyTemplate();
- RaiseKeyEvent(target, Key.Enter, 0);
- Assert.Equal("Test", target.Text);
- }
- }
- [Theory]
- [InlineData(new object[] { false, TextWrapping.NoWrap, ScrollBarVisibility.Hidden })]
- [InlineData(new object[] { false, TextWrapping.Wrap, ScrollBarVisibility.Disabled })]
- [InlineData(new object[] { true, TextWrapping.NoWrap, ScrollBarVisibility.Auto })]
- [InlineData(new object[] { true, TextWrapping.Wrap, ScrollBarVisibility.Disabled })]
- public void Has_Correct_Horizontal_ScrollBar_Visibility(
- bool acceptsReturn,
- TextWrapping wrapping,
- ScrollBarVisibility expected)
- {
- using (UnitTestApplication.Start(Services))
- {
- var target = new TextBox
- {
- AcceptsReturn = acceptsReturn,
- TextWrapping = wrapping,
- };
- Assert.Equal(expected, ScrollViewer.GetHorizontalScrollBarVisibility(target));
- }
- }
- [Fact]
- public void SelectionEnd_Doesnt_Cause_Exception()
- {
- using (UnitTestApplication.Start(Services))
- {
- var target = new TextBox
- {
- Template = CreateTemplate(),
- Text = "0123456789"
- };
-
- target.ApplyTemplate();
- target.SelectionStart = 0;
- target.SelectionEnd = 9;
- target.Text = "123";
- RaiseTextEvent(target, "456");
- Assert.True(true);
- }
- }
- [Fact]
- public void SelectionStart_Doesnt_Cause_Exception()
- {
- using (UnitTestApplication.Start(Services))
- {
- var target = new TextBox
- {
- Template = CreateTemplate(),
- Text = "0123456789"
- };
-
- target.ApplyTemplate();
- target.SelectionStart = 8;
- target.SelectionEnd = 9;
- target.Text = "123";
- RaiseTextEvent(target, "456");
- Assert.True(true);
- }
- }
- [Fact]
- public void SelectionStartEnd_Are_Valid_AterTextChange()
- {
- using (UnitTestApplication.Start(Services))
- {
- var target = new TextBox
- {
- Template = CreateTemplate(),
- Text = "0123456789"
- };
- target.SelectionStart = 8;
- target.SelectionEnd = 9;
- target.Text = "123";
- Assert.True(target.SelectionStart <= "123".Length);
- Assert.True(target.SelectionEnd <= "123".Length);
- }
- }
- [Fact]
- public void SelectedText_Changes_OnSelectionChange()
- {
- using (UnitTestApplication.Start(Services))
- {
- var target = new TextBox
- {
- Template = CreateTemplate(),
- Text = "0123456789"
- };
-
- target.ApplyTemplate();
- Assert.True(target.SelectedText == "");
- target.SelectionStart = 2;
- target.SelectionEnd = 4;
- Assert.True(target.SelectedText == "23");
- }
- }
- [Fact]
- public void SelectedText_EditsText()
- {
- using (UnitTestApplication.Start(Services))
- {
- var target = new TextBox
- {
- Template = CreateTemplate(),
- Text = "0123"
- };
-
- target.ApplyTemplate();
- target.SelectedText = "AA";
- Assert.True(target.Text == "AA0123");
- target.SelectionStart = 1;
- target.SelectionEnd = 3;
- target.SelectedText = "BB";
- Assert.True(target.Text == "ABB123");
- }
- }
- [Fact]
- public void SelectedText_CanClearText()
- {
- using (UnitTestApplication.Start(Services))
- {
- var target = new TextBox
- {
- Template = CreateTemplate(),
- Text = "0123"
- };
- target.SelectionStart = 1;
- target.SelectionEnd = 3;
- target.SelectedText = "";
- Assert.True(target.Text == "03");
- }
- }
- [Fact]
- public void SelectedText_NullClearsText()
- {
- using (UnitTestApplication.Start(Services))
- {
- var target = new TextBox
- {
- Template = CreateTemplate(),
- Text = "0123"
- };
- target.SelectionStart = 1;
- target.SelectionEnd = 3;
- target.SelectedText = null;
- Assert.True(target.Text == "03");
- }
- }
- [Fact]
- public void CoerceCaretIndex_Doesnt_Cause_Exception_with_malformed_line_ending()
- {
- using (UnitTestApplication.Start(Services))
- {
- var target = new TextBox
- {
- Template = CreateTemplate(),
- Text = "0123456789\r"
- };
- target.CaretIndex = 11;
- Assert.True(true);
- }
- }
-
- [Theory]
- [InlineData(Key.Up)]
- [InlineData(Key.Down)]
- [InlineData(Key.Home)]
- [InlineData(Key.End)]
- public void Textbox_doesnt_crash_when_Receives_input_and_template_not_applied(Key key)
- {
- using (UnitTestApplication.Start(FocusServices))
- {
- var target1 = new TextBox
- {
- Template = CreateTemplate(),
- Text = "1234",
- };
- var root = new TestRoot { Child = target1 };
- target1.Focus();
- Assert.True(target1.IsFocused);
- RaiseKeyEvent(target1, key, KeyModifiers.None);
- }
- }
- [Fact]
- public void TextBox_GotFocus_And_LostFocus_Work_Properly()
- {
- using (UnitTestApplication.Start(FocusServices))
- {
- var target1 = new TextBox
- {
- Template = CreateTemplate(),
- Text = "1234"
- };
- var target2 = new TextBox
- {
- Template = CreateTemplate(),
- Text = "5678"
- };
- var sp = new StackPanel();
- sp.Children.Add(target1);
- sp.Children.Add(target2);
- target1.ApplyTemplate();
- target2.ApplyTemplate();
-
- var root = new TestRoot { Child = sp };
- var gfcount = 0;
- var lfcount = 0;
- target1.GotFocus += (s, e) => gfcount++;
- target2.LostFocus += (s, e) => lfcount++;
- target2.Focus();
- Assert.False(target1.IsFocused);
- Assert.True(target2.IsFocused);
- target1.Focus();
- Assert.False(target2.IsFocused);
- Assert.True(target1.IsFocused);
- Assert.Equal(1, gfcount);
- Assert.Equal(1, lfcount);
- }
- }
-
- [Fact]
- public void TextBox_CaretIndex_Persists_When_Focus_Lost()
- {
- using (UnitTestApplication.Start(FocusServices))
- {
- var target1 = new TextBox
- {
- Template = CreateTemplate(),
- Text = "1234"
- };
- var target2 = new TextBox
- {
- Template = CreateTemplate(),
- Text = "5678"
- };
- var sp = new StackPanel();
- sp.Children.Add(target1);
- sp.Children.Add(target2);
- target1.ApplyTemplate();
- target2.ApplyTemplate();
-
- var root = new TestRoot { Child = sp };
- target2.Focus();
- target2.CaretIndex = 2;
- Assert.False(target1.IsFocused);
- Assert.True(target2.IsFocused);
- target1.Focus();
-
- Assert.Equal(2, target2.CaretIndex);
- }
- }
-
- [Fact]
- public void TextBox_Reveal_Password_Reset_When_Lost_Focus()
- {
- using (UnitTestApplication.Start(FocusServices))
- {
- var target1 = new TextBox
- {
- Template = CreateTemplate(),
- Text = "1234",
- PasswordChar = '*'
- };
- var target2 = new TextBox
- {
- Template = CreateTemplate(),
- Text = "5678"
- };
- var sp = new StackPanel();
- sp.Children.Add(target1);
- sp.Children.Add(target2);
- target1.ApplyTemplate();
- target2.ApplyTemplate();
-
- var root = new TestRoot { Child = sp };
- target1.Focus();
- target1.RevealPassword = true;
-
- target2.Focus();
-
- Assert.False(target1.RevealPassword);
- }
- }
- [Fact]
- public void Setting_Bound_Text_To_Null_Works()
- {
- using (UnitTestApplication.Start(Services))
- {
- var source = new Class1 { Bar = "bar" };
- var target = new TextBox { Template = CreateTemplate(), DataContext = source };
- target.ApplyTemplate();
- target.Bind(TextBox.TextProperty, new Binding("Bar"));
- Assert.Equal("bar", target.Text);
- source.Bar = null;
- Assert.Null(target.Text);
- }
- }
-
- [Theory]
- [InlineData("abc", "d", 3, 0, 0, false, "abc")]
- [InlineData("abc", "dd", 4, 3, 3, false, "abcd")]
- [InlineData("abc", "ddd", 3, 0, 2, true, "ddc")]
- [InlineData("abc", "dddd", 4, 1, 3, true, "addd")]
- [InlineData("abc", "ddddd", 5, 3, 3, true, "abcdd")]
- public async Task MaxLength_Works_Properly(
- string initalText,
- string textInput,
- int maxLength,
- int selectionStart,
- int selectionEnd,
- bool fromClipboard,
- string expected)
- {
- using (UnitTestApplication.Start(Services))
- {
- var target = new TextBox
- {
- Template = CreateTemplate(),
- Text = initalText,
- MaxLength = maxLength,
- SelectionStart = selectionStart,
- SelectionEnd = selectionEnd
- };
- var impl = CreateMockTopLevelImpl();
- var topLevel = new TestTopLevel(impl.Object)
- {
- Template = CreateTopLevelTemplate()
- };
- topLevel.Content = target;
- topLevel.ApplyTemplate();
- topLevel.LayoutManager.ExecuteInitialLayoutPass();
- target.Measure(Size.Infinity);
-
- if (fromClipboard)
- {
- await topLevel.Clipboard!.SetTextAsync(textInput);
- RaiseKeyEvent(target, Key.V, KeyModifiers.Control);
- await topLevel.Clipboard!.ClearAsync();
- }
- else
- {
- RaiseTextEvent(target, textInput);
- }
-
- Assert.Equal(expected, target.Text);
- }
- }
- [Theory]
- [InlineData(Key.X, KeyModifiers.Control)]
- [InlineData(Key.Back, KeyModifiers.None)]
- [InlineData(Key.Delete, KeyModifiers.None)]
- [InlineData(Key.Tab, KeyModifiers.None)]
- [InlineData(Key.Enter, KeyModifiers.None)]
- public void Keys_Allow_Undo(Key key, KeyModifiers modifiers)
- {
- using (UnitTestApplication.Start(Services))
- {
- var target = new TextBox
- {
- Template = CreateTemplate(),
- Text = "0123",
- AcceptsReturn = true,
- AcceptsTab = true
- };
- var impl = CreateMockTopLevelImpl();
- var topLevel = new TestTopLevel(impl.Object)
- {
- Template = CreateTopLevelTemplate()
- };
- topLevel.Content = target;
- topLevel.ApplyTemplate();
- topLevel.LayoutManager.ExecuteInitialLayoutPass();
- target.ApplyTemplate();
- target.SelectionStart = 1;
- target.SelectionEnd = 3;
- RaiseKeyEvent(target, key, modifiers);
- RaiseKeyEvent(target, Key.Z, KeyModifiers.Control); // undo
- Assert.True(target.Text == "0123");
- }
- }
- [Fact]
- public void Setting_SelectedText_Should_Fire_Single_Text_Changed_Notification()
- {
- using (UnitTestApplication.Start(Services))
- {
- var target = new TextBox
- {
- Template = CreateTemplate(),
- Text = "0123",
- AcceptsReturn = true,
- AcceptsTab = true,
- SelectionStart = 1,
- SelectionEnd = 3,
- };
- var values = new List<string?>();
- target.GetObservable(TextBox.TextProperty).Subscribe(x => values.Add(x));
- target.SelectedText = "A";
- Assert.Equal(new[] { "0123", "0A3" }, values);
- }
- }
- [Fact]
- public void Entering_Text_With_SelectedText_Should_Fire_Single_Text_Changed_Notification()
- {
- using (UnitTestApplication.Start(Services))
- {
- var target = new TextBox
- {
- Template = CreateTemplate(),
- Text = "0123",
- AcceptsReturn = true,
- AcceptsTab = true,
- SelectionStart = 1,
- SelectionEnd = 3,
- };
- var values = new List<string?>();
- target.GetObservable(TextBox.TextProperty).Subscribe(x => values.Add(x));
- RaiseTextEvent(target, "A");
- Assert.Equal(new[] { "0123", "0A3" }, values);
- }
- }
- [Fact]
- public void Insert_Multiline_Text_Should_Accept_Extra_Lines_When_AcceptsReturn_Is_True()
- {
- using (UnitTestApplication.Start(Services))
- {
- var target = new TextBox
- {
- AcceptsReturn = true
- };
- RaiseTextEvent(target, $"123 {Environment.NewLine}456");
- Assert.Equal($"123 {Environment.NewLine}456", target.Text);
- }
- }
- [Fact]
- public void Insert_Multiline_Text_Should_Discard_Extra_Lines_When_AcceptsReturn_Is_False()
- {
- using (UnitTestApplication.Start(Services))
- {
- var target = new TextBox
- {
- AcceptsReturn = false
- };
- RaiseTextEvent(target, $"123 {"\r"}456");
- Assert.Equal("123 ", target.Text);
- target.Text = "";
- RaiseTextEvent(target, $"123 {"\r\n"}456");
- Assert.Equal("123 ", target.Text);
- }
- }
- [Fact]
- public async Task Should_Fullfill_MaxLines_Contraint()
- {
- using (UnitTestApplication.Start(Services))
- {
- var target = new TextBox
- {
- Template = CreateTemplate(),
- Text = "ABC",
- MaxLines = 1,
- AcceptsReturn = true
- };
- var impl = CreateMockTopLevelImpl();
- var topLevel = new TestTopLevel(impl.Object)
- {
- Template = CreateTopLevelTemplate()
- };
- topLevel.Content = target;
- topLevel.ApplyTemplate();
- topLevel.LayoutManager.ExecuteInitialLayoutPass();
- target.ApplyTemplate();
- target.Measure(Size.Infinity);
- var initialHeight = target.DesiredSize.Height;
- await topLevel.Clipboard!.SetTextAsync(Environment.NewLine);
- RaiseKeyEvent(target, Key.V, KeyModifiers.Control);
- await topLevel.Clipboard!.ClearAsync();
- RaiseTextEvent(target, Environment.NewLine);
- target.InvalidateMeasure();
- target.Measure(Size.Infinity);
- Assert.Equal(initialHeight, target.DesiredSize.Height);
- }
- }
- [Theory]
- [InlineData(1)]
- [InlineData(2)]
- [InlineData(3)]
- public void MaxLines_Sets_ScrollViewer_MaxHeight(int maxLines)
- {
- using (UnitTestApplication.Start(Services))
- {
- var target = new TextBox
- {
- Template = CreateTemplate(),
- MaxLines = maxLines,
- // Define explicit whole number line height for predictable calculations
- LineHeight = 20
- };
- var impl = CreateMockTopLevelImpl();
- var topLevel = new TestTopLevel(impl.Object)
- {
- Template = CreateTopLevelTemplate(),
- Content = target
- };
- topLevel.ApplyTemplate();
- topLevel.LayoutManager.ExecuteInitialLayoutPass();
- var textPresenter = target.FindDescendantOfType<TextPresenter>();
- Assert.NotNull(textPresenter);
- Assert.Equal("PART_TextPresenter", textPresenter.Name);
- Assert.Equal(new Thickness(0), textPresenter.Margin); // Test assumes no margin on TextPresenter
- var scrollViewer = target.FindDescendantOfType<ScrollViewer>();
- Assert.NotNull(scrollViewer);
- Assert.Equal("PART_ScrollViewer", scrollViewer.Name);
- Assert.Equal(maxLines * target.LineHeight, scrollViewer.MaxHeight);
- }
- }
- [Theory]
- [InlineData(1)]
- [InlineData(2)]
- [InlineData(3)]
- public void MaxLines_Sets_ScrollViewer_MaxHeight_With_TextPresenter_Margin(int maxLines)
- {
- using (UnitTestApplication.Start(Services))
- {
- var target = new TextBox
- {
- Template = CreateTemplate(),
- MaxLines = maxLines,
- // Define explicit whole number line height for predictable calculations
- LineHeight = 20
- };
- var impl = CreateMockTopLevelImpl();
- var topLevel = new TestTopLevel(impl.Object)
- {
- Template = CreateTopLevelTemplate(),
- Content = target
- };
- topLevel.ApplyTemplate();
- topLevel.LayoutManager.ExecuteInitialLayoutPass();
- var textPresenter = target.FindDescendantOfType<TextPresenter>();
- Assert.NotNull(textPresenter);
- Assert.Equal("PART_TextPresenter", textPresenter.Name);
- var textPresenterMargin = new Thickness(horizontal: 0, vertical: 3);
- textPresenter.Margin = textPresenterMargin;
- target.InvalidateMeasure();
- target.Measure(Size.Infinity);
- var scrollViewer = target.FindDescendantOfType<ScrollViewer>();
- Assert.NotNull(scrollViewer);
- Assert.Equal("PART_ScrollViewer", scrollViewer.Name);
- Assert.Equal((maxLines * target.LineHeight) + textPresenterMargin.Top + textPresenterMargin.Bottom, scrollViewer.MaxHeight);
- }
- }
- [Fact]
- public void Should_Fullfill_MinLines_Contraint()
- {
- using (UnitTestApplication.Start(Services))
- {
- var target = new TextBox
- {
- Template = CreateTemplate(),
- Text = "ABC \n DEF \n GHI",
- MinLines = 3,
- AcceptsReturn = true
- };
- var impl = CreateMockTopLevelImpl();
- var topLevel = new TestTopLevel(impl.Object)
- {
- Template = CreateTopLevelTemplate()
- };
- topLevel.Content = target;
- topLevel.ApplyTemplate();
- topLevel.LayoutManager.ExecuteInitialLayoutPass();
- target.ApplyTemplate();
- target.Measure(Size.Infinity);
- var initialHeight = target.DesiredSize.Height;
- target.Text = "";
- target.InvalidateMeasure();
- target.Measure(Size.Infinity);
- Assert.Equal(initialHeight, target.DesiredSize.Height);
- }
- }
- [Theory]
- [InlineData(1)]
- [InlineData(2)]
- [InlineData(3)]
- public void MinLines_Sets_ScrollViewer_MinHeight(int minLines)
- {
- using (UnitTestApplication.Start(Services))
- {
- var target = new TextBox
- {
- Template = CreateTemplate(),
- MinLines = minLines,
- // Define explicit whole number line height for predictable calculations
- LineHeight = 20
- };
- var impl = CreateMockTopLevelImpl();
- var topLevel = new TestTopLevel(impl.Object)
- {
- Template = CreateTopLevelTemplate(),
- Content = target
- };
- topLevel.ApplyTemplate();
- topLevel.LayoutManager.ExecuteInitialLayoutPass();
- var textPresenter = target.FindDescendantOfType<TextPresenter>();
- Assert.NotNull(textPresenter);
- Assert.Equal("PART_TextPresenter", textPresenter.Name);
- Assert.Equal(new Thickness(0), textPresenter.Margin); // Test assumes no margin on TextPresenter
- var scrollViewer = target.FindDescendantOfType<ScrollViewer>();
- Assert.NotNull(scrollViewer);
- Assert.Equal("PART_ScrollViewer", scrollViewer.Name);
- Assert.Equal(minLines * target.LineHeight, scrollViewer.MinHeight);
- }
- }
- [Theory]
- [InlineData(1)]
- [InlineData(2)]
- [InlineData(3)]
- public void MinLines_Sets_ScrollViewer_MinHeight_With_TextPresenter_Margin(int minLines)
- {
- using (UnitTestApplication.Start(Services))
- {
- var target = new TextBox
- {
- Template = CreateTemplate(),
- MinLines = minLines,
- // Define explicit whole number line height for predictable calculations
- LineHeight = 20
- };
- var impl = CreateMockTopLevelImpl();
- var topLevel = new TestTopLevel(impl.Object)
- {
- Template = CreateTopLevelTemplate(),
- Content = target
- };
- topLevel.ApplyTemplate();
- topLevel.LayoutManager.ExecuteInitialLayoutPass();
- var textPresenter = target.FindDescendantOfType<TextPresenter>();
- Assert.NotNull(textPresenter);
- Assert.Equal("PART_TextPresenter", textPresenter.Name);
- var textPresenterMargin = new Thickness(horizontal: 0, vertical: 3);
- textPresenter.Margin = textPresenterMargin;
- target.InvalidateMeasure();
- target.Measure(Size.Infinity);
- var scrollViewer = target.FindDescendantOfType<ScrollViewer>();
- Assert.NotNull(scrollViewer);
- Assert.Equal("PART_ScrollViewer", scrollViewer.Name);
- Assert.Equal((minLines * target.LineHeight) + textPresenterMargin.Top + textPresenterMargin.Bottom, scrollViewer.MinHeight);
- }
- }
-
- [Theory]
- [InlineData(null, 1)]
- [InlineData("", 1)]
- [InlineData("Hello", 1)]
- [InlineData("Hello\r\nWorld", 2)]
- public void LineCount_Is_Correct(string? text, int lineCount)
- {
- using (UnitTestApplication.Start(Services))
- {
- var target = new TextBox
- {
- Template = CreateTemplate(),
- Text = text,
- AcceptsReturn = true
- };
- var impl = CreateMockTopLevelImpl();
- var topLevel = new TestTopLevel(impl.Object)
- {
- Template = CreateTopLevelTemplate()
- };
- topLevel.Content = target;
- topLevel.ApplyTemplate();
- topLevel.LayoutManager.ExecuteInitialLayoutPass();
- target.ApplyTemplate();
- target.Measure(Size.Infinity);
- Assert.Equal(lineCount, target.GetLineCount());
- }
- }
- [Fact]
- public void Unmeasured_TextBox_Has_Negative_LineCount()
- {
- var b = new TextBox();
- Assert.Equal(-1, b.GetLineCount());
- }
-
- [Fact]
- public void LineCount_Is_Correct_After_Text_Change()
- {
- using (UnitTestApplication.Start(Services))
- {
- var target = new TextBox
- {
- Template = CreateTemplate(),
- Text = "Hello",
- AcceptsReturn = true
- };
- var impl = CreateMockTopLevelImpl();
- var topLevel = new TestTopLevel(impl.Object)
- {
- Template = CreateTopLevelTemplate()
- };
- topLevel.Content = target;
- topLevel.ApplyTemplate();
- topLevel.LayoutManager.ExecuteInitialLayoutPass();
- target.ApplyTemplate();
- target.Measure(Size.Infinity);
-
- Assert.Equal(1, target.GetLineCount());
- target.Text = "Hello\r\nWorld";
- Assert.Equal(2, target.GetLineCount());
- }
- }
- [Fact]
- public void Visible_LineCount_DoesNot_Affect_LineCount()
- {
- using (UnitTestApplication.Start(Services))
- {
- var target = new TextBox
- {
- Template = CreateTemplate(),
- Text = "Hello\r\nWorld\r\nHello\r\nAvalonia",
- AcceptsReturn = true,
- MaxLines = 2,
- };
- var impl = CreateMockTopLevelImpl();
- var topLevel = new TestTopLevel(impl.Object)
- {
- Template = CreateTopLevelTemplate()
- };
- topLevel.Content = target;
- topLevel.ApplyTemplate();
- topLevel.LayoutManager.ExecuteInitialLayoutPass();
- target.ApplyTemplate();
- target.Measure(Size.Infinity);
- Assert.Equal(4, target.GetLineCount());
- }
- }
- [Fact]
- public void CanUndo_CanRedo_Is_False_When_Initialized()
- {
- using (UnitTestApplication.Start(Services))
- {
- var tb = new TextBox
- {
- Template = CreateTemplate(),
- Text = "New Text"
- };
- tb.Measure(Size.Infinity);
- Assert.False(tb.CanUndo);
- Assert.False(tb.CanRedo);
- }
- }
- [Fact]
- public void CanUndo_CanRedo_and_Programmatic_Undo_Redo_Works()
- {
- using (UnitTestApplication.Start(Services))
- {
- var tb = new TextBox
- {
- Template = CreateTemplate(),
- };
- tb.Measure(Size.Infinity);
- // See GH #6024 for a bit more insight on when Undo/Redo snapshots are taken:
- // - Every 'Space', but only when space is handled in OnKeyDown - Spaces in TextInput event won't work
- // - Every 7 chars in a long word
- RaiseTextEvent(tb, "ABC");
- RaiseKeyEvent(tb, Key.Space, KeyModifiers.None);
- RaiseTextEvent(tb, "DEF");
- RaiseKeyEvent(tb, Key.Space, KeyModifiers.None);
- RaiseTextEvent(tb, "123");
- // NOTE: the spaces won't actually add spaces b/c they're sent only as key events and not Text events
- // so our final text is without spaces
- Assert.Equal("ABCDEF123", tb.Text);
- Assert.True(tb.CanUndo);
- tb.Undo();
- // Undo will take us back one step
- Assert.Equal("ABCDEF", tb.Text);
- Assert.True(tb.CanRedo);
- tb.Redo();
- // Redo should restore us
- Assert.Equal("ABCDEF123", tb.Text);
- }
- }
- [Fact]
- public void Setting_UndoLimit_Clears_Undo_Redo()
- {
- using (UnitTestApplication.Start(Services))
- {
- var tb = new TextBox
- {
- Template = CreateTemplate(),
- };
- tb.Measure(Size.Infinity);
- // This is all the same as the above test (CanUndo_CanRedo_and_Programmatic_Undo_Redo_Works)
- // We do this to get the undo/redo stacks in a state where both are active
- RaiseTextEvent(tb, "ABC");
- RaiseKeyEvent(tb, Key.Space, KeyModifiers.None);
- RaiseTextEvent(tb, "DEF");
- RaiseKeyEvent(tb, Key.Space, KeyModifiers.None);
- RaiseTextEvent(tb, "123");
- Assert.Equal("ABCDEF123", tb.Text);
- Assert.True(tb.CanUndo);
- tb.Undo();
- // Undo will take us back one step
- Assert.Equal("ABCDEF", tb.Text);
- Assert.True(tb.CanRedo);
- tb.Redo();
- // Redo should restore us
- Assert.Equal("ABCDEF123", tb.Text);
- // Change the undo limit, this should clear both stacks setting CanUndo and CanRedo to false
- tb.UndoLimit = 1;
- Assert.False(tb.CanUndo);
- Assert.False(tb.CanRedo);
- }
- }
- [Fact]
- public void Setting_IsUndoEnabled_To_False_Clears_Undo_Redo()
- {
- using (UnitTestApplication.Start(Services))
- {
- var tb = new TextBox
- {
- Template = CreateTemplate(),
- };
- tb.Measure(Size.Infinity);
- // This is all the same as the above test (CanUndo_CanRedo_and_Programmatic_Undo_Redo_Works)
- // We do this to get the undo/redo stacks in a state where both are active
- RaiseTextEvent(tb, "ABC");
- RaiseKeyEvent(tb, Key.Space, KeyModifiers.None);
- RaiseTextEvent(tb, "DEF");
- RaiseKeyEvent(tb, Key.Space, KeyModifiers.None);
- RaiseTextEvent(tb, "123");
- Assert.Equal("ABCDEF123", tb.Text);
- Assert.True(tb.CanUndo);
- tb.Undo();
- // Undo will take us back one step
- Assert.Equal("ABCDEF", tb.Text);
- Assert.True(tb.CanRedo);
- tb.Redo();
- // Redo should restore us
- Assert.Equal("ABCDEF123", tb.Text);
- // Disable Undo/Redo, this should clear both stacks setting CanUndo and CanRedo to false
- tb.IsUndoEnabled = false;
- Assert.False(tb.CanUndo);
- Assert.False(tb.CanRedo);
- }
- }
- [Fact]
- public void UndoLimit_Count_Is_Respected()
- {
- using (UnitTestApplication.Start(Services))
- {
- var tb = new TextBox
- {
- Template = CreateTemplate(),
- UndoLimit = 3 // Something small for this test
- };
- tb.Measure(Size.Infinity);
- // Push 3 undoable actions, we should only be able to recover 2
- RaiseTextEvent(tb, "ABC");
- RaiseKeyEvent(tb, Key.Space, KeyModifiers.None);
- RaiseTextEvent(tb, "DEF");
- RaiseKeyEvent(tb, Key.Space, KeyModifiers.None);
- RaiseTextEvent(tb, "123");
- Assert.Equal("ABCDEF123", tb.Text);
- // Undo will take us back one step
- tb.Undo();
- Assert.Equal("ABCDEF", tb.Text);
- // Undo again
- tb.Undo();
- Assert.Equal("ABC", tb.Text);
- // We now should not be able to undo again
- Assert.False(tb.CanUndo);
- }
- }
- [Fact]
- public void Should_Move_Caret_To_EndOfLine()
- {
- using (UnitTestApplication.Start(Services))
- {
- var tb = new TextBox
- {
- Template = CreateTemplate(),
- Text = "AB\nAB"
- };
- tb.Measure(Size.Infinity);
- RaiseKeyEvent(tb, Key.End, KeyModifiers.Shift);
- Assert.Equal(2, tb.CaretIndex);
- }
- }
- [Theory]
- [InlineData(2,4)]
- [InlineData(0,4)]
- [InlineData(2,6)]
- [InlineData(0,6)]
- [InlineData(3,4)]
- public void When_Selection_From_Left_To_Right_Pressing_Right_Should_Remove_Selection_Moving_Caret_To_End_Of_Previous_Selection(int selectionStart, int selectionEnd)
- {
- using (UnitTestApplication.Start(Services))
- {
- var tb = new TextBox
- {
- Template = CreateTemplate(),
- Text = "ABCDEF"
- };
- tb.Measure(Size.Infinity);
- tb.CaretIndex = selectionStart;
- tb.SelectionStart = selectionStart;
- tb.SelectionEnd = selectionEnd;
- RaiseKeyEvent(tb, Key.Right, KeyModifiers.None);
- Assert.Equal(selectionEnd, tb.SelectionStart);
- Assert.Equal(selectionEnd, tb.SelectionEnd);
- Assert.Equal(selectionEnd, tb.CaretIndex);
- }
- }
- [Theory]
- [InlineData(2,4)]
- [InlineData(0,4)]
- [InlineData(2,6)]
- [InlineData(0,6)]
- [InlineData(3,4)]
- public void When_Selection_From_Left_To_Right_Pressing_Left_Should_Remove_Selection_Moving_Caret_To_Start_Of_Previous_Selection(int selectionStart, int selectionEnd)
- {
- using (UnitTestApplication.Start(Services))
- {
- var tb = new TextBox
- {
- Template = CreateTemplate(),
- Text = "ABCDEF"
- };
- tb.Measure(Size.Infinity);
- tb.CaretIndex = selectionStart;
- tb.SelectionStart = selectionStart;
- tb.SelectionEnd = selectionEnd;
- RaiseKeyEvent(tb, Key.Left, KeyModifiers.None);
- Assert.Equal(selectionStart, tb.SelectionStart);
- Assert.Equal(selectionStart, tb.SelectionEnd);
- Assert.Equal(selectionStart, tb.CaretIndex);
- }
- }
- [Theory]
- [InlineData(4,2)]
- [InlineData(4,0)]
- [InlineData(6,2)]
- [InlineData(6,0)]
- [InlineData(4,3)]
- public void When_Selection_From_Right_To_Left_Pressing_Right_Should_Remove_Selection_Moving_Caret_To_Start_Of_Previous_Selection(int selectionStart, int selectionEnd)
- {
- using (UnitTestApplication.Start(Services))
- {
- var tb = new TextBox
- {
- Template = CreateTemplate(),
- Text = "ABCDEF"
- };
- tb.Measure(Size.Infinity);
- tb.CaretIndex = selectionStart;
- tb.SelectionStart = selectionStart;
- tb.SelectionEnd = selectionEnd;
- RaiseKeyEvent(tb, Key.Right, KeyModifiers.None);
- Assert.Equal(selectionStart, tb.SelectionStart);
- Assert.Equal(selectionStart, tb.SelectionEnd);
- Assert.Equal(selectionStart, tb.CaretIndex);
- }
- }
- [Theory]
- [InlineData(4,2)]
- [InlineData(4,0)]
- [InlineData(6,2)]
- [InlineData(6,0)]
- [InlineData(4,3)]
- public void When_Selection_From_Right_To_Left_Pressing_Left_Should_Remove_Selection_Moving_Caret_To_End_Of_Previous_Selection(int selectionStart, int selectionEnd)
- {
- using (UnitTestApplication.Start(Services))
- {
- var tb = new TextBox
- {
- Template = CreateTemplate(),
- Text = "ABCDEF"
- };
- tb.Measure(Size.Infinity);
- tb.CaretIndex = selectionStart;
- tb.SelectionStart = selectionStart;
- tb.SelectionEnd = selectionEnd;
- RaiseKeyEvent(tb, Key.Left, KeyModifiers.None);
- Assert.Equal(selectionEnd, tb.SelectionStart);
- Assert.Equal(selectionEnd, tb.SelectionEnd);
- Assert.Equal(selectionEnd, tb.CaretIndex);
- }
- }
- [Theory]
- [InlineData(0)]
- [InlineData(2)]
- [InlineData(4)]
- [InlineData(6)]
- public void When_Select_All_From_Position_Left_Should_Remove_Selection_Moving_Caret_To_Start(int caretIndex)
- {
- using (UnitTestApplication.Start(Services))
- {
- var tb = new TextBox
- {
- Template = CreateTemplate(),
- Text = "ABCDEF"
- };
- tb.Measure(Size.Infinity);
- tb.CaretIndex = caretIndex;
- RaiseKeyEvent(tb, Key.A, KeyModifiers.Control);
- RaiseKeyEvent(tb, Key.Left, KeyModifiers.None);
- Assert.Equal(0, tb.SelectionStart);
- Assert.Equal(0, tb.SelectionEnd);
- Assert.Equal(0, tb.CaretIndex);
- }
- }
- [Theory]
- [InlineData(0)]
- [InlineData(2)]
- [InlineData(4)]
- [InlineData(6)]
- public void When_Select_All_From_Position_Right_Should_Remove_Selection_Moving_Caret_To_End(int caretIndex)
- {
- using (UnitTestApplication.Start(Services))
- {
- var tb = new TextBox
- {
- Template = CreateTemplate(),
- Text = "ABCDEF"
- };
- tb.Measure(Size.Infinity);
- tb.CaretIndex = caretIndex;
- RaiseKeyEvent(tb, Key.A, KeyModifiers.Control);
- RaiseKeyEvent(tb, Key.Right, KeyModifiers.None);
- Assert.Equal(tb.Text.Length, tb.SelectionStart);
- Assert.Equal(tb.Text.Length, tb.SelectionEnd);
- Assert.Equal(tb.Text.Length, tb.CaretIndex);
- }
- }
- [Theory]
- [InlineData(2,4)]
- [InlineData(0,4)]
- [InlineData(2,6)]
- [InlineData(0,6)]
- [InlineData(3,4)]
- public void When_Selection_From_Left_To_Right_Pressing_Up_Should_Remove_Selection_Moving_Caret_To_Start_Of_Previous_Selection(int selectionStart, int selectionEnd)
- {
- using (UnitTestApplication.Start(Services))
- {
- var tb = new TextBox
- {
- Template = CreateTemplate(),
- Text = "ABCDEF"
- };
- tb.Measure(Size.Infinity);
- tb.CaretIndex = selectionStart;
- tb.SelectionStart = selectionStart;
- tb.SelectionEnd = selectionEnd;
- RaiseKeyEvent(tb, Key.Up, KeyModifiers.None);
- Assert.Equal(selectionStart, tb.SelectionStart);
- Assert.Equal(selectionStart, tb.SelectionEnd);
- Assert.Equal(selectionStart, tb.CaretIndex);
- }
- }
- [Theory]
- [InlineData(4,2)]
- [InlineData(4,0)]
- [InlineData(6,2)]
- [InlineData(6,0)]
- [InlineData(4,3)]
- public void When_Selection_From_Right_To_Left_Pressing_Up_Should_Remove_Selection_Moving_Caret_To_End_Of_Previous_Selection(int selectionStart, int selectionEnd)
- {
- using (UnitTestApplication.Start(Services))
- {
- var tb = new TextBox
- {
- Template = CreateTemplate(),
- Text = "ABCDEF"
- };
- tb.Measure(Size.Infinity);
- tb.CaretIndex = selectionStart;
- tb.SelectionStart = selectionStart;
- tb.SelectionEnd = selectionEnd;
- RaiseKeyEvent(tb, Key.Up, KeyModifiers.None);
- Assert.Equal(selectionEnd, tb.SelectionStart);
- Assert.Equal(selectionEnd, tb.SelectionEnd);
- Assert.Equal(selectionEnd, tb.CaretIndex);
- }
- }
- [Theory]
- [InlineData(0)]
- [InlineData(2)]
- [InlineData(4)]
- [InlineData(6)]
- public void When_Select_All_From_Position_Up_Should_Remove_Selection_Moving_Caret_To_Start(int caretIndex)
- {
- using (UnitTestApplication.Start(Services))
- {
- var tb = new TextBox
- {
- Template = CreateTemplate(),
- Text = "ABCDEF"
- };
- tb.Measure(Size.Infinity);
- tb.CaretIndex = caretIndex;
- RaiseKeyEvent(tb, Key.A, KeyModifiers.Control);
- RaiseKeyEvent(tb, Key.Up, KeyModifiers.None);
- Assert.Equal(0, tb.SelectionStart);
- Assert.Equal(0, tb.SelectionEnd);
- Assert.Equal(0, tb.CaretIndex);
- }
- }
- [Theory]
- [InlineData(2,4)]
- [InlineData(0,4)]
- [InlineData(2,6)]
- [InlineData(0,6)]
- [InlineData(3,4)]
- public void When_Selection_From_Left_To_Right_Pressing_Down_Should_Remove_Selection_Moving_Caret_To_End_Of_Previous_Selection(int selectionStart, int selectionEnd)
- {
- using (UnitTestApplication.Start(Services))
- {
- var tb = new TextBox
- {
- Template = CreateTemplate(),
- Text = "ABCDEF"
- };
- tb.Measure(Size.Infinity);
- tb.CaretIndex = selectionStart;
- tb.SelectionStart = selectionStart;
- tb.SelectionEnd = selectionEnd;
- RaiseKeyEvent(tb, Key.Down, KeyModifiers.None);
- Assert.Equal(selectionEnd, tb.SelectionStart);
- Assert.Equal(selectionEnd, tb.SelectionEnd);
- Assert.Equal(selectionEnd, tb.CaretIndex);
- }
- }
- [Theory]
- [InlineData(4,2)]
- [InlineData(4,0)]
- [InlineData(6,2)]
- [InlineData(6,0)]
- [InlineData(4,3)]
- public void When_Selection_From_Right_To_Left_Pressing_Down_Should_Remove_Selection_Moving_Caret_To_Start_Of_Previous_Selection(int selectionStart, int selectionEnd)
- {
- using (UnitTestApplication.Start(Services))
- {
- var tb = new TextBox
- {
- Template = CreateTemplate(),
- Text = "ABCDEF"
- };
- tb.Measure(Size.Infinity);
- tb.CaretIndex = selectionStart;
- tb.SelectionStart = selectionStart;
- tb.SelectionEnd = selectionEnd;
- RaiseKeyEvent(tb, Key.Down, KeyModifiers.None);
- Assert.Equal(selectionStart, tb.SelectionStart);
- Assert.Equal(selectionStart, tb.SelectionEnd);
- Assert.Equal(selectionStart, tb.CaretIndex);
- }
- }
- [Theory]
- [InlineData(0)]
- [InlineData(2)]
- [InlineData(4)]
- [InlineData(6)]
- public void When_Select_All_From_Position_Down_Should_Remove_Selection_Moving_Caret_To_End(int caretIndex)
- {
- using (UnitTestApplication.Start(Services))
- {
- var tb = new TextBox
- {
- Template = CreateTemplate(),
- Text = "ABCDEF"
- };
- tb.Measure(Size.Infinity);
- tb.CaretIndex = caretIndex;
- RaiseKeyEvent(tb, Key.A, KeyModifiers.Control);
- RaiseKeyEvent(tb, Key.Down, KeyModifiers.None);
- Assert.Equal(tb.Text.Length, tb.SelectionStart);
- Assert.Equal(tb.Text.Length, tb.SelectionEnd);
- Assert.Equal(tb.Text.Length, tb.CaretIndex);
- }
- }
- [Fact]
- public void TextBox_In_AdornerLayer_Will_Not_Cause_Collection_Modified_In_VisualLayerManager_Measure()
- {
- using (UnitTestApplication.Start(Services))
- {
- var button = new Button();
- var root = new TestRoot()
- {
- Child = new VisualLayerManager()
- {
- Child = button
- }
- };
- var adorner = new TextBox { Template = CreateTemplate(), Text = "a" };
- var adornerLayer = AdornerLayer.GetAdornerLayer(button);
- Assert.NotNull(adornerLayer);
- adornerLayer.Children.Add(adorner);
- AdornerLayer.SetAdornedElement(adorner, button);
- root.Measure(Size.Infinity);
- }
- }
- [Fact]
- public void TextBox_In_AdornerLayer_Will_Not_Cause_Collection_Modified_In_VisualLayerManager_Arrange()
- {
- using (UnitTestApplication.Start(Services))
- {
- var button = new Button();
- var visualLayerManager = new VisualLayerManager() { Child = button };
- var root = new TestRoot()
- {
- Child = visualLayerManager
- };
- var adorner = new TextBox { Template = CreateTemplate(), Text = "a" };
- var adornerLayer = AdornerLayer.GetAdornerLayer(button);
- Assert.NotNull(adornerLayer);
- root.Measure(new Size(10, 10));
- adornerLayer.Children.Add(adorner);
- AdornerLayer.SetAdornedElement(adorner, button);
- root.Arrange(new Rect(0, 0, 10, 10));
- }
- }
- [Theory]
- [InlineData("A\nBB\nCCC\nDDDD", 0, 0)]
- [InlineData("A\nBB\nCCC\nDDDD", 1, 2)]
- [InlineData("A\nBB\nCCC\nDDDD", 2, 5)]
- [InlineData("A\nBB\nCCC\nDDDD", 3, 9)]
- [InlineData("واحد\nاثنين\nثلاثة\nأربعة", 0, 0)]
- [InlineData("واحد\nاثنين\nثلاثة\nأربعة", 1, 5)]
- [InlineData("واحد\nاثنين\nثلاثة\nأربعة", 2, 11)]
- [InlineData("واحد\nاثنين\nثلاثة\nأربعة", 3, 17)]
- public void Should_Scroll_Caret_To_Line(string text, int targetLineIndex, int expectedCaretIndex)
- {
- using (UnitTestApplication.Start(Services))
- {
- var tb = new TextBox
- {
- Template = CreateTemplate(),
- Text = text
- };
- tb.ApplyTemplate();
- tb.ScrollToLine(targetLineIndex);
- Assert.Equal(expectedCaretIndex, tb.CaretIndex);
- }
- }
- [Fact]
- public void Should_Throw_ArgumentOutOfRange()
- {
- using (UnitTestApplication.Start(Services))
- {
- var tb = new TextBox
- {
- Template = CreateTemplate(),
- Text = string.Empty
- };
- tb.ApplyTemplate();
- Assert.Throws<ArgumentOutOfRangeException>(() => tb.ScrollToLine(-1));
- Assert.Throws<ArgumentOutOfRangeException>(() => tb.ScrollToLine(1));
- }
- }
- [Fact]
- public void InputMethodClient_SurroundingText_Returns_Empty_For_Empty_Line()
- {
- using var _ = UnitTestApplication.Start(Services);
- var textBox = new TextBox
- {
- Template = CreateTemplate(),
- Text = "",
- CaretIndex = 0
- };
- textBox.ApplyTemplate();
- var eventArgs = new TextInputMethodClientRequestedEventArgs
- {
- RoutedEvent = InputElement.TextInputMethodClientRequestedEvent
- };
- textBox.RaiseEvent(eventArgs);
- var client = eventArgs.Client;
- Assert.NotNull(client);
- Assert.Equal(string.Empty, client.SurroundingText);
- }
-
- [Fact]
- public void Backspace_Should_Delete_Last_Character_In_Line_And_Keep_Caret_On_Same_Line()
- {
- using var _ = UnitTestApplication.Start(Services);
- var textBox = new TextBox
- {
- Template = CreateTemplate(),
- Text = "a\nb",
- CaretIndex = 3
- };
- textBox.ApplyTemplate();
- var topLevel = new TestTopLevel(CreateMockTopLevelImpl().Object)
- {
- Template = CreateTopLevelTemplate(),
- Content = textBox
- };
- topLevel.ApplyTemplate();
- topLevel.LayoutManager.ExecuteInitialLayoutPass();
- var textPresenter = textBox.FindDescendantOfType<TextPresenter>();
- Assert.NotNull(textPresenter);
- var oldCaretY = textPresenter.GetCursorRectangle().Top;
- Assert.NotEqual(0, oldCaretY);
- RaiseKeyEvent(textBox, Key.Back, KeyModifiers.None);
- Assert.Equal("a\n", textBox.Text);
- Assert.Equal(2, textBox.CaretIndex);
- Assert.Equal(2, textPresenter.CaretIndex);
- var caretY = textPresenter.GetCursorRectangle().Top;
- Assert.Equal(oldCaretY, caretY);
- }
- [Fact]
- public void Losing_Focus_Should_Not_Reset_Selection()
- {
- using (UnitTestApplication.Start(FocusServices))
- {
- var target1 = new TextBox
- {
- Template = CreateTemplate(),
- Text = "1234",
- ClearSelectionOnLostFocus = false
- };
- target1.ApplyTemplate();
- var target2 = new TextBox
- {
- Template = CreateTemplate(),
- };
- target2.ApplyTemplate();
- var sp = new StackPanel();
- sp.Children.Add(target1);
- sp.Children.Add(target2);
- var root = new TestRoot() { Child = sp };
- target1.SelectionStart = 0;
- target1.SelectionEnd = 4;
- target1.Focus();
- Assert.True(target1.IsFocused);
- Assert.Equal("1234", target1.SelectedText);
- target2.Focus();
- Assert.Equal("1234", target1.SelectedText);
- }
- }
- private static TestServices FocusServices => TestServices.MockThreadingInterface.With(
- focusManager: new FocusManager(),
- keyboardDevice: () => new KeyboardDevice(),
- keyboardNavigation: () => new KeyboardNavigationHandler(),
- inputManager: new InputManager(),
- standardCursorFactory: Mock.Of<ICursorFactory>(),
- textShaperImpl: new HeadlessTextShaperStub(),
- fontManagerImpl: new HeadlessFontManagerStub());
- private static TestServices Services => TestServices.MockThreadingInterface.With(
- standardCursorFactory: Mock.Of<ICursorFactory>(),
- renderInterface: new HeadlessPlatformRenderInterface(),
- textShaperImpl: new HeadlessTextShaperStub(),
- fontManagerImpl: new HeadlessFontManagerStub());
- internal static IControlTemplate CreateTemplate()
- {
- return new FuncControlTemplate<TextBox>((control, scope) =>
- new ScrollViewer
- {
- Name = "PART_ScrollViewer",
- Template = new FuncControlTemplate<ScrollViewer>(ScrollViewerTests.CreateTemplate),
- Content = new TextPresenter
- {
- Name = "PART_TextPresenter",
- [!!TextPresenter.TextProperty] = new Binding
- {
- Path = nameof(TextPresenter.Text),
- Mode = BindingMode.TwoWay,
- Priority = BindingPriority.Template,
- RelativeSource = new RelativeSource(RelativeSourceMode.TemplatedParent),
- },
- [!!TextPresenter.CaretIndexProperty] = new Binding
- {
- Path = nameof(TextPresenter.CaretIndex),
- Mode = BindingMode.TwoWay,
- Priority = BindingPriority.Template,
- RelativeSource = new RelativeSource(RelativeSourceMode.TemplatedParent),
- }
- }.RegisterInNameScope(scope)
- }.RegisterInNameScope(scope));
- }
- private static void RaiseKeyEvent(TextBox textBox, Key key, KeyModifiers inputModifiers)
- {
- textBox.RaiseEvent(new KeyEventArgs
- {
- RoutedEvent = InputElement.KeyDownEvent,
- KeyModifiers = inputModifiers,
- Key = key
- });
- }
- private static void RaiseTextEvent(TextBox textBox, string text)
- {
- textBox.RaiseEvent(new TextInputEventArgs
- {
- RoutedEvent = InputElement.TextInputEvent,
- Text = text
- });
- }
- private class Class1 : NotifyingBase
- {
- private int _foo;
- private string? _bar;
- public int Foo
- {
- get { return _foo; }
- set { _foo = value; RaisePropertyChanged(); }
- }
- public string? Bar
- {
- get { return _bar; }
- set { _bar = value; RaisePropertyChanged(); }
- }
- }
- private class TestTopLevel : TopLevel
- {
- private readonly ILayoutManager _layoutManager;
- public TestTopLevel(ITopLevelImpl impl, ILayoutManager? layoutManager = null)
- : base(impl)
- {
- _layoutManager = layoutManager ?? new LayoutManager(this);
- }
- private protected override ILayoutManager CreateLayoutManager() => _layoutManager;
- }
- private static Mock<ITopLevelImpl> CreateMockTopLevelImpl()
- {
- var clipboard = new Mock<ITopLevelImpl>();
- clipboard.Setup(x => x.Compositor).Returns(RendererMocks.CreateDummyCompositor());
- clipboard.Setup(r => r.TryGetFeature(typeof(IClipboard)))
- .Returns(new HeadlessClipboardStub());
- clipboard.SetupGet(x => x.RenderScaling).Returns(1);
- return clipboard;
- }
- private static FuncControlTemplate<TestTopLevel> CreateTopLevelTemplate()
- {
- return new FuncControlTemplate<TestTopLevel>((x, scope) =>
- new ContentPresenter
- {
- Name = "PART_ContentPresenter",
- [!ContentPresenter.ContentProperty] = x[!ContentControl.ContentProperty],
- }.RegisterInNameScope(scope));
- }
- private class TestContextMenu : ContextMenu
- {
- public TestContextMenu()
- {
- IsOpen = true;
- }
- }
- }
- }
|