@@ -30,6 +30,8 @@ const sessionActionSurface = source('entry/src/main/ets/pages/components/Session
3030const sessionDetailsView = source ( 'entry/src/main/ets/pages/components/SessionDetailsView.ets' ) ;
3131const conversationViewSettings = source ( 'entry/src/main/ets/pages/components/ConversationViewSettings.ets' ) ;
3232const welcomeHome = source ( 'entry/src/main/ets/pages/components/WelcomeHome.ets' ) ;
33+ const remoteSurfaceHost = source ( 'entry/src/main/ets/pages/components/remote/RemoteSurfaceHost.ets' ) ;
34+ const wideConversationHost = source ( 'entry/src/main/ets/pages/components/WideConversationHost.ets' ) ;
3335
3436// Reads one @Builder out of a component, so an assertion can name the layer it
3537// is about instead of counting matches in the whole file.
@@ -78,13 +80,19 @@ test('the shell runs the window full-screen for every page', () => {
7880 'the welcome page must not switch the window layout any more' ) ;
7981} ) ;
8082
81- test ( 'the navigation bar is transparent and the status bar keeps the page colour' , ( ) => {
82- // The fade is what should be visible where the navigation bar sits, and the
83- // header band keeps its opaque page colour so the top does not regress.
83+ test ( 'both system bars are transparent so the page owns their strips' , ( ) => {
84+ // The window runs full-screen, so the status bar's strip is the page's own
85+ // top edge: the drawer's fill and the chat page's blurred header band are what
86+ // should show there. An opaque bar painted the page colour over both — it
87+ // clipped the sidebar's title — and it also sat over the pull-down
88+ // notification centre's frosted background as a white strip. The content
89+ // colour is what keeps the clock and the icons legible either way.
8490 assert . match ( entryAbility , / n a v i g a t i o n B a r C o l o r : M o b i l e D e s i g n C o l o r s \. t r a n s p a r e n t \. l i g h t / ,
8591 'the navigation bar must be transparent so the page bottom shows through' ) ;
86- assert . match ( entryAbility , / s t a t u s B a r C o l o r : b a c k g r o u n d / ,
87- 'the status bar must keep the page colour' ) ;
92+ assert . match ( entryAbility , / s t a t u s B a r C o l o r : M o b i l e D e s i g n C o l o r s \. t r a n s p a r e n t \. l i g h t / ,
93+ 'the status bar must be transparent so the page top shows through' ) ;
94+ assert . match ( entryAbility , / s t a t u s B a r C o n t e n t C o l o r : c o n t e n t / ,
95+ 'the status bar icons must keep following the colour mode' ) ;
8896} ) ;
8997
9098test ( 'the window inset service is the only place the strip is measured' , ( ) => {
@@ -131,6 +139,45 @@ test('the chat page header band reserves the status bar itself', () => {
131139 'the band that carries the inset must stay the one that paints the header' ) ;
132140} ) ;
133141
142+ test ( 'every surface whose top edge is the screen edge reserves the status bar' , ( ) => {
143+ // Immersive layout moved every page's origin to the top of the window, so the
144+ // first row of a full-screen surface is what has to keep clear of the status
145+ // bar. The band that owns the surface's fill carries the padding, which is
146+ // what keeps that fill reaching the top of the window while the controls start
147+ // below the clock and the indicators: the drawer panel's title, the gallery's
148+ // back control, the preview's header and the home headers' drawer control are
149+ // all one strip lower than they were while the page area ended below it.
150+ assert . match ( normalize ( builderBody ( appSidebar , 'SidebarContent' ) ) ,
151+ / \. p a d d i n g \( \{ l e f t : 2 0 , r i g h t : 2 0 , t o p : t h i s \. i n s e t s \. t o p \} \) / ,
152+ 'the drawer panel must reserve the strip above its title' ) ;
153+ assert . match ( normalize ( miniAppSurface ) ,
154+ / \. h e i g h t \( 5 6 \+ t h i s \. i n s e t s \. t o p \) \. p a d d i n g \( \{ l e f t : 1 2 , r i g h t : 1 2 , t o p : t h i s \. i n s e t s \. t o p \} \) / ,
155+ 'the mini-app gallery header must reserve the strip' ) ;
156+ assert . match ( normalize ( filePreviewSurface ) ,
157+ / \. h e i g h t \( 6 8 \+ t h i s \. i n s e t s \. t o p \) [ \s \S ] * ?\. p a d d i n g \( \{ l e f t : 8 , r i g h t : 8 , t o p : 8 \+ t h i s \. i n s e t s \. t o p , b o t t o m : 8 \} \) / ,
158+ 'the file preview header must reserve the strip' ) ;
159+ assert . match ( normalize ( remoteSurfaceHost ) ,
160+ / \. p a d d i n g \( \{ t o p : t h i s \. i n s e t s \. t o p \} \) / ,
161+ 'the compact home header must reserve the strip' ) ;
162+ assert . match ( normalize ( remoteSurfaceHost ) ,
163+ / \. h e i g h t \( 7 6 \+ t h i s \. i n s e t s \. t o p \) \. p a d d i n g \( \{ l e f t : 1 6 , r i g h t : 1 6 , t o p : 1 4 \+ t h i s \. i n s e t s \. t o p , b o t t o m : 1 2 \} \) / ,
164+ 'the wide home header must reserve the strip' ) ;
165+ // A floating control on a pane whose top edge is the window's keeps the strip
166+ // clear itself, the way the surfaces' fixed bottom controls keep the
167+ // navigation bar clear.
168+ assert . match ( normalize ( wideConversationHost ) ,
169+ / \. p o s i t i o n \( \{ x : 1 2 , y : 1 2 \+ t h i s \. i n s e t s \. t o p \} \) / ,
170+ 'the floating master-restore control must reserve the strip' ) ;
171+ for ( const [ name , text ] of [ [ 'RemoteSurfaceHost' , remoteSurfaceHost ] ,
172+ [ 'WideConversationHost' , wideConversationHost ] ] ) {
173+ assert . match ( text , / @ L o c a l i n s e t s : W i n d o w I n s e t s B i n d i n g = n e w W i n d o w I n s e t s B i n d i n g \( \) ; / ,
174+ `${ name } must hold the shared inset binding` ) ;
175+ assert . match ( text , / t h i s \. i n s e t s \. b i n d \( t h i s \. g e t U I C o n t e x t \( \) , c o n t e x t \) / ,
176+ `${ name } must bind the insets while it is mounted` ) ;
177+ assert . match ( text , / t h i s \. i n s e t s \. u n b i n d \( \) / , `${ name } must release the insets when it goes` ) ;
178+ }
179+ } ) ;
180+
134181test ( 'the settings sheet viewport reaches the screen edge and its rows end in a tail spacer' , ( ) => {
135182 // The sheet is a bindSheet, which the framework does not inset while the
136183 // window is immersive, so its own box decides where content can scroll. The
@@ -171,8 +218,8 @@ test('the sidebar list scrolls under the bar and its floating footer keeps the s
171218 // the footer keeps its distance from that edge itself, and the list ends in
172219 // a tail spacer so its last row rests above the navigation bar.
173220 const content = normalize ( builderBody ( appSidebar , 'SidebarContent' ) ) ;
174- assert . match ( content , / \. p a d d i n g \( \{ l e f t : 2 0 , r i g h t : 2 0 , t o p : 0 \} \) / ,
175- 'the panel root must not shrink its scrolling viewport' ) ;
221+ assert . match ( content , / \. p a d d i n g \( \{ l e f t : 2 0 , r i g h t : 2 0 , t o p : t h i s \. i n s e t s \. t o p \} \) / ,
222+ 'the panel root must reserve the status bar without shrinking its scrolling viewport' ) ;
176223 assert . match ( content , / \. p a d d i n g \( \{ b o t t o m : t h i s \. s c r o l l T a i l P a d d i n g \( \) \} \) / ,
177224 'the session list must end in a tail spacer' ) ;
178225 assert . match ( content , / \. m a r g i n \( \{ b o t t o m : t h i s \. f o o t e r B o t t o m P a d d i n g \( \) \} \) / ,
0 commit comments