1# Application Window Development (Stage Model) 2 3 4## Basic Concepts 5 6- Immersive window: a window display mode where the system windows (generally the status bar and navigation bar) are hidden to allow users to fully engage with the content. 7 8 The immersive window feature is applicable only to the main window of an application in full-screen mode. It does not apply to a main window in freeform window mode or a subwindow (for example, a dialog box or a floating window). 9 10- Floating window: a special application window that can still be displayed in the foreground when the main window and corresponding ability are running in the background. 11 12 The floating window can be used to continue playing a video after the application is switched to the background, or offer a quick entry (for example, bubbles) to the application. Before creating a floating window, an application must apply for the required permission. 13 14 15## When to Use 16 17In the stage model, you can perform the following operations during application window development: 18 19- Setting the properties and content of the main window of an application 20 21- Setting the properties and content of the subwindow of an application 22 23- Experiencing the immersive window feature 24 25- Setting a floating window 26 27- Listening for interactive and non-interactive window events 28 29## Available APIs 30 31The table below lists the common APIs used for application window development. For details about more APIs, see [Window](../reference/apis-arkui/js-apis-window.md). 32 33| Instance | API | Description | 34| -------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | 35| WindowStage | getMainWindow(callback: AsyncCallback<Window>): void | Obtains the main window of this window stage.<br>This API can be used only in the stage model.| 36| WindowStage | loadContent(path: string, callback: AsyncCallback<void>): void | Loads content to the main window in this window stage.<br>**path**: path of the page from which the content will be loaded. The path is configured in the **main_pages.json** file of the project.<br>This API can be used only in the stage model.| 37| WindowStage | createSubWindow(name: string, callback: AsyncCallback<Window>): void | Creates a subwindow.<br>This API can be used only in the stage model. | 38| WindowStage | on(type: 'windowStageEvent', callback: Callback<WindowStageEventType>): void | Subscribes to window stage lifecycle change events.<br>This API can be used only in the stage model.| 39| Window static method| createWindow(config: Configuration, callback: AsyncCallback\<Window>): void | Creates a subwindow or system window.<br>**config**: parameters used for creating the window. | 40| Window | setUIContent(path: string, callback: AsyncCallback<void>): void | Loads the content of a page, with its path in the current project specified, to this window.<br>**path**: path of the page from which the content will be loaded. The path is configured in the **main_pages.json** file of the project in the stage model. | 41| Window | setWindowBrightness(brightness: number, callback: AsyncCallback<void>): void | Sets the brightness for this window. | 42| Window | setWindowTouchable(isTouchable: boolean, callback: AsyncCallback<void>): void | Sets whether this window is touchable. | 43| Window | moveWindowTo(x: number, y: number, callback: AsyncCallback<void>): void | Moves this window. | 44| Window | resize(width: number, height: number, callback: AsyncCallback<void>): void | Changes the window size. | 45| Window | setWindowLayoutFullScreen(isLayoutFullScreen: boolean): Promise<void> | Sets whether to enable the full-screen mode for the window layout. | 46| Window | setWindowSystemBarEnable(names: Array<'status'\|'navigation'>): Promise<void> | Sets whether to display the status bar and navigation bar in this window. | 47| Window | setWindowSystemBarProperties(systemBarProperties: SystemBarProperties): Promise<void> | Sets the properties of the status bar and navigation bar in this window.<br>**systemBarProperties**: properties of the status bar and navigation bar.| 48| Window | showWindow(callback: AsyncCallback\<void>): void | Shows this window. | 49| Window | on(type: 'touchOutside', callback: Callback<void>): void | Subscribes to touch events outside this window. | 50| Window | destroyWindow(callback: AsyncCallback<void>): void | Destroys this window. | 51 52 53## Setting the Main Window of an Application 54 55In the stage model, the main window of an application is created and maintained by a **UIAbility** instance. In the **onWindowStageCreate** callback of the **UIAbility** instance, use **WindowStage** to obtain the main window of the application and set its properties. You can also set the properties (for example, **maxWindowWidth**) in the [module.json5 file](../quick-start/module-configuration-file.md#abilities). 56 57### How to Develop 58 591. Obtain the main window. 60 61 Call **getMainWindow** to obtain the main window of the application. 62 632. Set the properties of the main window. 64 65 You can set multiple properties of the main window, such as the background color, brightness, and whether the main window is touchable. The code snippet below uses the **touchable** property as an example. 66 673. Load content to the main window. 68 69 Call **loadContent** to load content to the main window. 70 71```ts 72import { UIAbility } from '@kit.AbilityKit'; 73import { window } from '@kit.ArkUI'; 74import { BusinessError } from '@kit.BasicServicesKit'; 75 76export default class EntryAbility extends UIAbility { 77 onWindowStageCreate(windowStage: window.WindowStage) { 78 // 1. Obtain the main window of the application. 79 let windowClass: window.Window | null = null; 80 windowStage.getMainWindow((err: BusinessError, data) => { 81 let errCode: number = err.code; 82 if (errCode) { 83 console.error('Failed to obtain the main window. Cause: ' + JSON.stringify(err)); 84 return; 85 } 86 windowClass = data; 87 console.info('Succeeded in obtaining the main window. Data: ' + JSON.stringify(data)); 88 // 2. Set the touchable property of the main window. 89 let isTouchable: boolean = true; 90 windowClass.setWindowTouchable(isTouchable, (err: BusinessError) => { 91 let errCode: number = err.code; 92 if (errCode) { 93 console.error('Failed to set the window to be touchable. Cause:' + JSON.stringify(err)); 94 return; 95 } 96 console.info('Succeeded in setting the window to be touchable.'); 97 }) 98 }) 99 // 3. Load content to the main window. 100 windowStage.loadContent("pages/page2", (err: BusinessError) => { 101 let errCode: number = err.code; 102 if (errCode) { 103 console.error('Failed to load the content. Cause:' + JSON.stringify(err)); 104 return; 105 } 106 console.info('Succeeded in loading the content.'); 107 }); 108 } 109}; 110``` 111 112## Setting a Subwindow of an Application 113 114You can create an application subwindow, such as a dialog box, and set its properties. 115 116### How to Develop 117 1181. Create a subwindow. 119 120 Call **createSubWindow** to create a subwindow. 121 1222. Set the properties of the subwindow. 123 124 After the subwindow is created, you can set its properties, such as the size, position, background color, and brightness. 125 1263. Load content to and show the subwindow. 127 128 Call **setUIContent** to load content to the subwindow and **showWindow** to show the subwindow. 129 1304. Destroy the subwindow. 131 132 When the subwindow is no longer needed, you can call **destroyWindow** to destroy it. 133 134The code snippet for creating a subwindow in **onWindowStageCreate** is as follows: 135 136```ts 137import { UIAbility } from '@kit.AbilityKit'; 138import { window } from '@kit.ArkUI'; 139import { BusinessError } from '@kit.BasicServicesKit'; 140 141let windowStage_: window.WindowStage | null = null; 142let sub_windowClass: window.Window | null = null; 143 144export default class EntryAbility extends UIAbility { 145 showSubWindow() { 146 // 1. Create a subwindow. 147 if (windowStage_ == null) { 148 console.error('Failed to create the subwindow. Cause: windowStage_ is null'); 149 } 150 else { 151 windowStage_.createSubWindow("mySubWindow", (err: BusinessError, data) => { 152 let errCode: number = err.code; 153 if (errCode) { 154 console.error('Failed to create the subwindow. Cause: ' + JSON.stringify(err)); 155 return; 156 } 157 sub_windowClass = data; 158 console.info('Succeeded in creating the subwindow. Data: ' + JSON.stringify(data)); 159 // 2. Set the position, size, and other properties of the subwindow. 160 sub_windowClass.moveWindowTo(300, 300, (err: BusinessError) => { 161 let errCode: number = err.code; 162 if (errCode) { 163 console.error('Failed to move the window. Cause:' + JSON.stringify(err)); 164 return; 165 } 166 console.info('Succeeded in moving the window.'); 167 }); 168 sub_windowClass.resize(500, 500, (err: BusinessError) => { 169 let errCode: number = err.code; 170 if (errCode) { 171 console.error('Failed to change the window size. Cause:' + JSON.stringify(err)); 172 return; 173 } 174 console.info('Succeeded in changing the window size.'); 175 }); 176 // 3.1 Load content to the subwindow. 177 sub_windowClass.setUIContent("pages/page3", (err: BusinessError) => { 178 let errCode: number = err.code; 179 if (errCode) { 180 console.error('Failed to load the content. Cause:' + JSON.stringify(err)); 181 return; 182 } 183 console.info('Succeeded in loading the content.'); 184 // 3.2 Show the subwindow. 185 (sub_windowClass as window.Window).showWindow((err: BusinessError) => { 186 let errCode: number = err.code; 187 if (errCode) { 188 console.error('Failed to show the window. Cause: ' + JSON.stringify(err)); 189 return; 190 } 191 console.info('Succeeded in showing the window.'); 192 }); 193 }); 194 }) 195 } 196 } 197 198 destroySubWindow() { 199 // 4. Destroy the subwindow when it is no longer needed (depending on the service logic). 200 (sub_windowClass as window.Window).destroyWindow((err: BusinessError) => { 201 let errCode: number = err.code; 202 if (errCode) { 203 console.error('Failed to destroy the window. Cause: ' + JSON.stringify(err)); 204 return; 205 } 206 console.info('Succeeded in destroying the window.'); 207 }); 208 } 209 210 onWindowStageCreate(windowStage: window.WindowStage) { 211 windowStage_ = windowStage; 212 // Create a subwindow when it is needed, for example, when a touch event occurs in the main window. Calling onWindowStageCreate is not always necessary. The code here is for reference only. 213 this.showSubWindow(); 214 } 215 216 onWindowStageDestroy() { 217 // Destroy the subwindow when it is no longer needed, for example, when the Close button in the subwindow is touched. Calling onWindowStageDestroy is not always necessary. The code here is for reference only. 218 this.destroySubWindow(); 219 } 220}; 221``` 222 223You can also click a button on a page to create a subwindow. The code snippet is as follows: 224 225```ts 226// EntryAbility.ets 227onWindowStageCreate(windowStage: window.WindowStage) { 228 windowStage.loadContent('pages/Index', (err) => { 229 if (err.code) { 230 console.error('Failed to load the content. Cause:' + JSON.stringify(err)); 231 return; 232 } 233 console.info('Succeeded in loading the content.'); 234 }) 235 236 // Transfer the window stage to the Index page. 237 AppStorage.setOrCreate('windowStage', windowStage); 238} 239``` 240 241```ts 242// Index.ets 243import { window } from '@kit.ArkUI'; 244import { BusinessError } from '@kit.BasicServicesKit'; 245 246let windowStage_: window.WindowStage | undefined = undefined; 247let sub_windowClass: window.Window | undefined = undefined; 248@Entry 249@Component 250struct Index { 251 @State message: string = 'Hello World'; 252 private CreateSubWindow(){ 253 // Obtain the window stage. 254 windowStage_ = AppStorage.get('windowStage'); 255 // 1. Create a subwindow. 256 if (windowStage_ == null) { 257 console.error('Failed to create the subwindow. Cause: windowStage_ is null'); 258 } 259 else { 260 windowStage_.createSubWindow("mySubWindow", (err: BusinessError, data) => { 261 let errCode: number = err.code; 262 if (errCode) { 263 console.error('Failed to create the subwindow. Cause: ' + JSON.stringify(err)); 264 return; 265 } 266 sub_windowClass = data; 267 console.info('Succeeded in creating the subwindow. Data: ' + JSON.stringify(data)); 268 // 2. Set the position, size, and other properties of the subwindow. 269 sub_windowClass.moveWindowTo(300, 300, (err: BusinessError) => { 270 let errCode: number = err.code; 271 if (errCode) { 272 console.error('Failed to move the window. Cause:' + JSON.stringify(err)); 273 return; 274 } 275 console.info('Succeeded in moving the window.'); 276 }); 277 sub_windowClass.resize(500, 500, (err: BusinessError) => { 278 let errCode: number = err.code; 279 if (errCode) { 280 console.error('Failed to change the window size. Cause:' + JSON.stringify(err)); 281 return; 282 } 283 console.info('Succeeded in changing the window size.'); 284 }); 285 // 3. Load content to the subwindow. 286 sub_windowClass.setUIContent("pages/subWindow", (err: BusinessError) => { 287 let errCode: number = err.code; 288 if (errCode) { 289 console.error('Failed to load the content. Cause:' + JSON.stringify(err)); 290 return; 291 } 292 console.info('Succeeded in loading the content.'); 293 // 3. Show the subwindow. 294 (sub_windowClass as window.Window).showWindow((err: BusinessError) => { 295 let errCode: number = err.code; 296 if (errCode) { 297 console.error('Failed to show the window. Cause: ' + JSON.stringify(err)); 298 return; 299 } 300 console.info('Succeeded in showing the window.'); 301 }); 302 }); 303 }) 304 } 305 } 306 private destroySubWindow(){ 307 // 4. Destroy the subwindow when it is no longer needed (depending on the service logic). 308 (sub_windowClass as window.Window).destroyWindow((err: BusinessError) => { 309 let errCode: number = err.code; 310 if (errCode) { 311 console.error('Failed to destroy the window. Cause: ' + JSON.stringify(err)); 312 return; 313 } 314 console.info('Succeeded in destroying the window.'); 315 }); 316 } 317 build() { 318 Row() { 319 Column() { 320 Text(this.message) 321 .fontSize(50) 322 .fontWeight(FontWeight.Bold) 323 Button(){ 324 Text('CreateSubWindow') 325 .fontSize(24) 326 .fontWeight(FontWeight.Normal) 327 }.width(220).height(68) 328 .margin({left:10, top:60}) 329 .onClick(() => { 330 this.CreateSubWindow() 331 }) 332 Button(){ 333 Text('destroySubWindow') 334 .fontSize(24) 335 .fontWeight(FontWeight.Normal) 336 }.width(220).height(68) 337 .margin({left:10, top:60}) 338 .onClick(() => { 339 this.destroySubWindow() 340 }) 341 } 342 .width('100%') 343 } 344 .height('100%') 345 } 346} 347``` 348 349```ts 350// subWindow.ets 351@Entry 352@Component 353struct SubWindow { 354 @State message: string = 'Hello subWindow'; 355 build() { 356 Row() { 357 Column() { 358 Text(this.message) 359 .fontSize(50) 360 .fontWeight(FontWeight.Bold) 361 } 362 .width('100%') 363 } 364 .height('100%') 365 } 366} 367``` 368 369## Experiencing the Immersive Window Feature 370 371To create a better video watching and gaming experience, you can use the immersive window feature to hide the status bar and navigation bar. This feature is available only for the main window of an application. Since API version 10, the immersive window has the same size as the full screen by default; its layout is controlled by the component module; the background color of its status bar and navigation bar is transparent, and the text color is black. When an application window calls **setWindowLayoutFullScreen**, with **true** passed in, an immersive window layout is used. If **false** is passed in, a non-immersive window layout is used. 372 373> **NOTE** 374> 375> Currently, immersive UI development supports window-level configuration, but not page-level configuration. If page redirection is required, you can set the immersive mode at the beginning of the page lifecycle, for example, in the **onPageShow** callback, and then restore the default settings when the page exits, for example, in the **onPageHide** callback. 376 377### How to Develop 378 3791. Obtain the main window. 380 381 Call **getMainWindow** to obtain the main window of the application. 382 3832. Implement the immersive effect. You can use either of the following methods: 384 385 - Method 1: When the main window of the application is a full-screen window, call **setWindowSystemBarEnable** to hide the status bar and navigation bar. 386 387 - Method 2: Call **setWindowLayoutFullScreen** to enable the full-screen mode for the main window layout. Call **setWindowSystemBarProperties** to set the opacity, background color, text color, and highlighted icon of the status bar and navigation bar to create a display effect consistent with that of the main window. 388 3893. Load content to the immersive window. 390 391 Call **loadContent** to load content to the immersive window. 392 393```ts 394import { UIAbility } from '@kit.AbilityKit'; 395import { window } from '@kit.ArkUI'; 396import { BusinessError } from '@kit.BasicServicesKit'; 397 398export default class EntryAbility extends UIAbility { 399 onWindowStageCreate(windowStage: window.WindowStage) { 400 // 1. Obtain the main window of the application. 401 let windowClass: window.Window | null = null; 402 windowStage.getMainWindow((err: BusinessError, data) => { 403 let errCode: number = err.code; 404 if (errCode) { 405 console.error('Failed to obtain the main window. Cause: ' + JSON.stringify(err)); 406 return; 407 } 408 windowClass = data; 409 console.info('Succeeded in obtaining the main window. Data: ' + JSON.stringify(data)); 410 411 // 2. Implement the immersive effect by hiding the status bar and navigation bar. 412 let names: Array<'status' | 'navigation'> = []; 413 windowClass.setWindowSystemBarEnable(names) 414 .then(() => { 415 console.info('Succeeded in setting the system bar to be visible.'); 416 }) 417 .catch((err: BusinessError) => { 418 console.error('Failed to set the system bar to be visible. Cause:' + JSON.stringify(err)); 419 }); 420 // 2. Alternatively, implement the immersive effect by setting the properties of the status bar and navigation bar. 421 let isLayoutFullScreen = true; 422 windowClass.setWindowLayoutFullScreen(isLayoutFullScreen) 423 .then(() => { 424 console.info('Succeeded in setting the window layout to full-screen mode.'); 425 }) 426 .catch((err: BusinessError) => { 427 console.error('Failed to set the window layout to full-screen mode. Cause:' + JSON.stringify(err)); 428 }); 429 let sysBarProps: window.SystemBarProperties = { 430 statusBarColor: '#ff00ff', 431 navigationBarColor: '#00ff00', 432 // The following properties are supported since API version 8. 433 statusBarContentColor: '#ffffff', 434 navigationBarContentColor: '#ffffff' 435 }; 436 windowClass.setWindowSystemBarProperties(sysBarProps) 437 .then(() => { 438 console.info('Succeeded in setting the system bar properties.'); 439 }) 440 .catch((err: BusinessError) => { 441 console.error('Failed to set the system bar properties. Cause: ' + JSON.stringify(err)); 442 }); 443 }) 444 // 3. Load content to the immersive window. 445 windowStage.loadContent("pages/page2", (err: BusinessError) => { 446 let errCode: number = err.code; 447 if (errCode) { 448 console.error('Failed to load the content. Cause:' + JSON.stringify(err)); 449 return; 450 } 451 console.info('Succeeded in loading the content.'); 452 }); 453 } 454}; 455``` 456 457<!--RP2--> 458## Setting a Floating Window<!--RP2End--> 459 460A floating window is created based on an existing task. It is always displayed in the foreground, even if the task used for creating the floating window is switched to the background. Generally, the floating window is above all application windows. You can create a floating window and set its properties. 461 462 463### How to Develop 464 465<!--RP1--> 466**Prerequisites**: To create a floating window (a window of the type **WindowType.TYPE_FLOAT**), you must request the **ohos.permission.SYSTEM_FLOAT_WINDOW** permission. For details, see [Requesting Permissions for system_basic Applications](../security/AccessToken/determine-application-mode.md#requesting-permissions-for-system_basic-applications). 467<!--RP1End--> 468 4691. Create a floating window. 470 471 Call **window.createWindow** to create a floating window. 472 4732. Set properties of the floating window. 474 475 After the floating window is created, you can set its properties, such as the size, position, background color, and brightness. 476 4773. Load content to and show the floating window. 478 479 Call **setUIContent** to load content to the floating window and **showWindow** to show the window. 480 4814. Destroy the floating window. 482 483 When the floating window is no longer needed, you can call **destroyWindow** to destroy it. 484 485```ts 486import { UIAbility } from '@kit.AbilityKit'; 487import { window } from '@kit.ArkUI'; 488import { BusinessError } from '@kit.BasicServicesKit'; 489 490export default class EntryAbility extends UIAbility { 491 onWindowStageCreate(windowStage: window.WindowStage) { 492 // 1. Create a floating window. 493 let windowClass: window.Window | null = null; 494 let config: window.Configuration = { 495 name: "floatWindow", windowType: window.WindowType.TYPE_FLOAT, ctx: this.context 496 }; 497 window.createWindow(config, (err: BusinessError, data) => { 498 let errCode: number = err.code; 499 if (errCode) { 500 console.error('Failed to create the floatWindow. Cause: ' + JSON.stringify(err)); 501 return; 502 } 503 console.info('Succeeded in creating the floatWindow. Data: ' + JSON.stringify(data)); 504 windowClass = data; 505 // 2. Set the position, size, and other properties of the floating window. 506 windowClass.moveWindowTo(300, 300, (err: BusinessError) => { 507 let errCode: number = err.code; 508 if (errCode) { 509 console.error('Failed to move the window. Cause:' + JSON.stringify(err)); 510 return; 511 } 512 console.info('Succeeded in moving the window.'); 513 }); 514 windowClass.resize(500, 500, (err: BusinessError) => { 515 let errCode: number = err.code; 516 if (errCode) { 517 console.error('Failed to change the window size. Cause:' + JSON.stringify(err)); 518 return; 519 } 520 console.info('Succeeded in changing the window size.'); 521 }); 522 // 3.1 Load content to the floating window. 523 windowClass.setUIContent("pages/page4", (err: BusinessError) => { 524 let errCode: number = err.code; 525 if (errCode) { 526 console.error('Failed to load the content. Cause:' + JSON.stringify(err)); 527 return; 528 } 529 console.info('Succeeded in loading the content.'); 530 // 3.2 Show the floating window. 531 (windowClass as window.Window).showWindow((err: BusinessError) => { 532 let errCode: number = err.code; 533 if (errCode) { 534 console.error('Failed to show the window. Cause: ' + JSON.stringify(err)); 535 return; 536 } 537 console.info('Succeeded in showing the window.'); 538 }); 539 }); 540 // 4. Destroy the floating window when it is no longer needed (depending on the service logic). 541 windowClass.destroyWindow((err: BusinessError) => { 542 let errCode: number = err.code; 543 if (errCode) { 544 console.error('Failed to destroy the window. Cause: ' + JSON.stringify(err)); 545 return; 546 } 547 console.info('Succeeded in destroying the window.'); 548 }); 549 }); 550 } 551}; 552``` 553 554## Listening for Interactive and Non-Interactive Window Events 555 556When running in the foreground, an application may switch between interactive and non-interactive states and process services depending on the state. For example, when the user opens the **Recents** screen, an application becomes non-interactive and pauses the service interaction with the user, such as video playback or camera preview; when the user switched back to the foreground, the application becomes interactive again, and the paused service needs to be resumed. 557 558### How to Develop 559 560After a **WindowStage** object is created, the application can listen for the **'windowStageEvent'** event to obtain window stage lifecycle changes, for example, whether the window stage is interactive or non-interactive in the foreground. The application can process services based on the reported event status. 561 562```ts 563import { UIAbility } from '@kit.AbilityKit'; 564import { window } from '@kit.ArkUI'; 565 566export default class EntryAbility extends UIAbility { 567 onWindowStageCreate(windowStage: window.WindowStage) { 568 try { 569 windowStage.on('windowStageEvent', (data) => { 570 console.info('Succeeded in enabling the listener for window stage event changes. Data: ' + 571 JSON.stringify(data)); 572 573 // Process services based on the event status. 574 if (data == window.WindowStageEventType.SHOWN) { 575 console.info('current window stage event is SHOWN'); 576 // The application enters the foreground and is interactive by default. 577 // ... 578 } else if (data == window.WindowStageEventType.HIDDEN) { 579 console.info('current window stage event is HIDDEN'); 580 // The application enters the background and is non-interactive by default. 581 // ... 582 } else if (data == window.WindowStageEventType.PAUSED) { 583 console.info('current window stage event is PAUSED'); 584 // The user opens the Recents screen when the application is running in the foreground, and the application becomes non-interactive. 585 // ... 586 } else if (data == window.WindowStageEventType.RESUMED) { 587 console.info('current window stage event is RESUMED'); 588 // The user switches back from the Recents screen to the application, and the application becomes interactive. 589 // ... 590 } 591 592 // ... 593 }); 594 } catch (exception) { 595 console.error('Failed to enable the listener for window stage event changes. Cause:' + 596 JSON.stringify(exception)); 597 } 598 } 599} 600``` 601