1e41f4b71Sopenharmony_ci# Window Title Bar Customization Development (ArkTS)
2e41f4b71Sopenharmony_ci
3e41f4b71Sopenharmony_ci## Overview
4e41f4b71Sopenharmony_ci
5e41f4b71Sopenharmony_ci### Introduction
6e41f4b71Sopenharmony_ci
7e41f4b71Sopenharmony_ciAs a crucial element of the GUI, the window title bar is located at the top of a window. It is intended to help users identify the purpose or content of the window, complete with some handy control options. OpenHarmony provides a default window title bar. You can customize it to match your needs. This topic provides guidance on customizing your title bar in ArkTS.
8e41f4b71Sopenharmony_ci
9e41f4b71Sopenharmony_ci### Constraints
10e41f4b71Sopenharmony_ci
11e41f4b71Sopenharmony_ciThe .js files that contain the custom title bar code must be placed in **foundation/arkui/ace_engine/frameworks/core/components_ng/pattern/container_modal/interfaces**.
12e41f4b71Sopenharmony_ci
13e41f4b71Sopenharmony_ciThe JS files must be built into ABC files with the **foundation/arkui/ace_engine/frameworks/core/components_ng/pattern/container_modal/interfaces/BUILD.gn** file.
14e41f4b71Sopenharmony_ci
15e41f4b71Sopenharmony_ciThe build configuration must be specified in **foundation/arkui/ace_engine/frameworks/core/components_ng/pattern/BUILD.gn**.
16e41f4b71Sopenharmony_ci
17e41f4b71Sopenharmony_ci## How to Develop
18e41f4b71Sopenharmony_ci
19e41f4b71Sopenharmony_ciIn this example, a custom system title bar is developed.
20e41f4b71Sopenharmony_ci
21e41f4b71Sopenharmony_ci1. Write the following code in an .ets file.
22e41f4b71Sopenharmony_ci    ```ts
23e41f4b71Sopenharmony_ci    import image from '@ohos.multimedia.image';
24e41f4b71Sopenharmony_ci
25e41f4b71Sopenharmony_ci    const TITLE_ICON_SIZE: string = '20vp'
26e41f4b71Sopenharmony_ci    const TITLE_PADDING_START: string = '20vp'
27e41f4b71Sopenharmony_ci    const TITLE_ELEMENT_MARGIN_HORIZONTAL: string = '12vp'
28e41f4b71Sopenharmony_ci    const TITLE_TEXT_FONT_SIZE: string = '16fp'
29e41f4b71Sopenharmony_ci    const TITLE_TEXT_FONT_WEIGHT: string = '500px'
30e41f4b71Sopenharmony_ci    const TITLE_ROW_HEIGHT: string = '37vp'
31e41f4b71Sopenharmony_ci
32e41f4b71Sopenharmony_ci    @Entry
33e41f4b71Sopenharmony_ci    @Component
34e41f4b71Sopenharmony_ci    struct Index {
35e41f4b71Sopenharmony_ci      @State appLabel: string = '';
36e41f4b71Sopenharmony_ci      @State appLabelColor: Color = 0xff000000;
37e41f4b71Sopenharmony_ci      @State appIcon: PixelMap | undefined = undefined;
38e41f4b71Sopenharmony_ci
39e41f4b71Sopenharmony_ci      // 1. Override the following methods to change the font color when the window obtains or loses focus.
40e41f4b71Sopenharmony_ci      onWindowFocused() {
41e41f4b71Sopenharmony_ci        this.appLabelColor = 0xff000000;
42e41f4b71Sopenharmony_ci      }
43e41f4b71Sopenharmony_ci
44e41f4b71Sopenharmony_ci      onWindowUnfocused() {
45e41f4b71Sopenharmony_ci        this.appLabelColor = 0x66000000;
46e41f4b71Sopenharmony_ci      }
47e41f4b71Sopenharmony_ci
48e41f4b71Sopenharmony_ci      // 2. Override the following methods for setting the application icon and name on the title bar.
49e41f4b71Sopenharmony_ci      setAppTitle(content: string ) {
50e41f4b71Sopenharmony_ci        this.appLabel = content;
51e41f4b71Sopenharmony_ci      }
52e41f4b71Sopenharmony_ci
53e41f4b71Sopenharmony_ci      setAppIcon(pixelMap: image.PixelMap) {
54e41f4b71Sopenharmony_ci        this.appIcon = pixelMap;
55e41f4b71Sopenharmony_ci      }
56e41f4b71Sopenharmony_ci
57e41f4b71Sopenharmony_ci      build() {
58e41f4b71Sopenharmony_ci        Row() {
59e41f4b71Sopenharmony_ci          // 3. Create an <Image> component for showing the application icon.
60e41f4b71Sopenharmony_ci          Image(this.appIcon)
61e41f4b71Sopenharmony_ci            .id("enhanceAppIcon")
62e41f4b71Sopenharmony_ci            .height(TITLE_ICON_SIZE)
63e41f4b71Sopenharmony_ci            .width(TITLE_ICON_SIZE)
64e41f4b71Sopenharmony_ci            .interpolation(ImageInterpolation.Medium)
65e41f4b71Sopenharmony_ci            .focusable(false)
66e41f4b71Sopenharmony_ci            .margin({ left: TITLE_PADDING_START, right: TITLE_ELEMENT_MARGIN_HORIZONTAL })
67e41f4b71Sopenharmony_ci          // 4. Create a <Text> component for showing the application name.
68e41f4b71Sopenharmony_ci          Text(this.appLabel)
69e41f4b71Sopenharmony_ci            .id("enhanceAppLabel")
70e41f4b71Sopenharmony_ci            .fontSize(TITLE_TEXT_FONT_SIZE)
71e41f4b71Sopenharmony_ci            .fontColor(this.appLabelColor)
72e41f4b71Sopenharmony_ci            .fontWeight(TITLE_TEXT_FONT_WEIGHT)
73e41f4b71Sopenharmony_ci            .maxLines(1)
74e41f4b71Sopenharmony_ci            .textOverflow({ overflow: TextOverflow.Ellipsis })
75e41f4b71Sopenharmony_ci            .textAlign(TextAlign.Start)
76e41f4b71Sopenharmony_ci            .layoutWeight(1.0)
77e41f4b71Sopenharmony_ci          }
78e41f4b71Sopenharmony_ci          .width('100%')
79e41f4b71Sopenharmony_ci          .height(TITLE_ROW_HEIGHT)
80e41f4b71Sopenharmony_ci          .justifyContent(FlexAlign.Start)
81e41f4b71Sopenharmony_ci          .alignItems(VerticalAlign.Center)
82e41f4b71Sopenharmony_ci          .padding({ top: 6, bottom: 6 })
83e41f4b71Sopenharmony_ci      }
84e41f4b71Sopenharmony_ci    }
85e41f4b71Sopenharmony_ci    ```
86e41f4b71Sopenharmony_ci2. Compile the .ets file into a .js file.
87e41f4b71Sopenharmony_ci    ```js
88e41f4b71Sopenharmony_ci    const TITLE_ICON_SIZE = '20vp';
89e41f4b71Sopenharmony_ci    const TITLE_PADDING_START = '20vp';
90e41f4b71Sopenharmony_ci    const TITLE_ELEMENT_MARGIN_HORIZONTAL = '12vp';
91e41f4b71Sopenharmony_ci    const TITLE_TEXT_FONT_SIZE = '16fp';
92e41f4b71Sopenharmony_ci    const TITLE_TEXT_FONT_WEIGHT = '500px';
93e41f4b71Sopenharmony_ci    const TITLE_ROW_HEIGHT = '37vp'
94e41f4b71Sopenharmony_ci    export class Index extends ViewPU {
95e41f4b71Sopenharmony_ci        constructor(parent, params, __localStorage, elmtId = -1) {
96e41f4b71Sopenharmony_ci            super(parent, __localStorage, elmtId);
97e41f4b71Sopenharmony_ci            this.__appLabel = new ObservedPropertySimplePU('', this, "appLabel");
98e41f4b71Sopenharmony_ci            this.__textColor = new ObservedPropertySimplePU(0xff000000, this, "textColor");
99e41f4b71Sopenharmony_ci            this.__pixelMap = new ObservedPropertyObjectPU(undefined, this, "appIcon");
100e41f4b71Sopenharmony_ci            this.setInitiallyProvidedValue(params);
101e41f4b71Sopenharmony_ci        }
102e41f4b71Sopenharmony_ci        setInitiallyProvidedValue(params) {
103e41f4b71Sopenharmony_ci            if (params.textColor !== undefined) {
104e41f4b71Sopenharmony_ci                this.textColor = params.textColor;
105e41f4b71Sopenharmony_ci            }
106e41f4b71Sopenharmony_ci            if (params.appLabel !== undefined) {
107e41f4b71Sopenharmony_ci                this.appLabel = params.appLabel;
108e41f4b71Sopenharmony_ci            }
109e41f4b71Sopenharmony_ci            if (params.appIcon !== undefined) {
110e41f4b71Sopenharmony_ci                this.appIcon = params.appIcon;
111e41f4b71Sopenharmony_ci            }
112e41f4b71Sopenharmony_ci        }
113e41f4b71Sopenharmony_ci        updateStateVars(params) {
114e41f4b71Sopenharmony_ci        }
115e41f4b71Sopenharmony_ci        purgeVariableDependenciesOnElmtId(rmElmtId) {
116e41f4b71Sopenharmony_ci            this.__textColor.purgeDependencyOnElmtId(rmElmtId);
117e41f4b71Sopenharmony_ci            this.__appLabel.purgeDependencyOnElmtId(rmElmtId);
118e41f4b71Sopenharmony_ci            this.__appIcon.purgeDependencyOnElmtId(rmElmtId);
119e41f4b71Sopenharmony_ci        }
120e41f4b71Sopenharmony_ci        aboutToBeDeleted() {
121e41f4b71Sopenharmony_ci            this.__textColor.aboutToBeDeleted();
122e41f4b71Sopenharmony_ci            this.__appLabel.aboutToBeDeleted();
123e41f4b71Sopenharmony_ci            this.__appIcon.aboutToBeDeleted();
124e41f4b71Sopenharmony_ci            SubscriberManager.Get().delete(this.id__());
125e41f4b71Sopenharmony_ci            this.aboutToBeDeletedInternal();
126e41f4b71Sopenharmony_ci        }
127e41f4b71Sopenharmony_ci        get textColor() {
128e41f4b71Sopenharmony_ci            return this.__textColor.get();
129e41f4b71Sopenharmony_ci        }
130e41f4b71Sopenharmony_ci        set textColor(newValue) {
131e41f4b71Sopenharmony_ci            this.__textColor.set(newValue);
132e41f4b71Sopenharmony_ci        }
133e41f4b71Sopenharmony_ci        get appLabel() {
134e41f4b71Sopenharmony_ci            return this.__appLabel.get();
135e41f4b71Sopenharmony_ci        }
136e41f4b71Sopenharmony_ci        set appLabel(newValue) {
137e41f4b71Sopenharmony_ci            this.__appLabel.set(newValue);
138e41f4b71Sopenharmony_ci        }
139e41f4b71Sopenharmony_ci        get appIcon() {
140e41f4b71Sopenharmony_ci            return this.__appIcon.get();
141e41f4b71Sopenharmony_ci        }
142e41f4b71Sopenharmony_ci        set appIcon(newValue) {
143e41f4b71Sopenharmony_ci            this.__appIcon.set(newValue);
144e41f4b71Sopenharmony_ci        }
145e41f4b71Sopenharmony_ci        onWindowFocused() {
146e41f4b71Sopenharmony_ci            this.textColor = 0xff000000;
147e41f4b71Sopenharmony_ci        }
148e41f4b71Sopenharmony_ci        onWindowUnfocused() {
149e41f4b71Sopenharmony_ci            this.textColor = 0x66000000;
150e41f4b71Sopenharmony_ci        }
151e41f4b71Sopenharmony_ci        setAppTitle(content) {
152e41f4b71Sopenharmony_ci            this.appLabel = content;
153e41f4b71Sopenharmony_ci        }
154e41f4b71Sopenharmony_ci        setAppIcon(pixelMap) {
155e41f4b71Sopenharmony_ci            this.appIcon = pixelMap;
156e41f4b71Sopenharmony_ci        }
157e41f4b71Sopenharmony_ci        initialRender() {
158e41f4b71Sopenharmony_ci            this.observeComponentCreation((elmtId, isInitialRender) => {
159e41f4b71Sopenharmony_ci                ViewStackProcessor.StartGetAccessRecordingFor(elmtId);
160e41f4b71Sopenharmony_ci                Row.create();
161e41f4b71Sopenharmony_ci                Row.width('100%');
162e41f4b71Sopenharmony_ci                Row.height(TITLE_ROW_HEIGHT);
163e41f4b71Sopenharmony_ci                Row.justifyContent(FlexAlign.Start);
164e41f4b71Sopenharmony_ci                Row.alignItems(VerticalAlign.Center);
165e41f4b71Sopenharmony_ci                Row.padding({ top: 6, bottom: 6 });
166e41f4b71Sopenharmony_ci                if (!isInitialRender) {
167e41f4b71Sopenharmony_ci                    Row.pop();
168e41f4b71Sopenharmony_ci                }
169e41f4b71Sopenharmony_ci                ViewStackProcessor.StopGetAccessRecording();
170e41f4b71Sopenharmony_ci            });
171e41f4b71Sopenharmony_ci            this.observeComponentCreation((elmtId, isInitialRender) => {
172e41f4b71Sopenharmony_ci                ViewStackProcessor.StartGetAccessRecordingFor(elmtId);
173e41f4b71Sopenharmony_ci                Image.create(this.appIcon);
174e41f4b71Sopenharmony_ci                Image.id("enhanceAppIcon");
175e41f4b71Sopenharmony_ci                Image.height(TITLE_ICON_SIZE);
176e41f4b71Sopenharmony_ci                Image.width(TITLE_ICON_SIZE);
177e41f4b71Sopenharmony_ci                Image.interpolation(ImageInterpolation.Medium);
178e41f4b71Sopenharmony_ci                Image.focusable(false);
179e41f4b71Sopenharmony_ci                Image.margin({ left: TITLE_PADDING_START, right: TITLE_ELEMENT_MARGIN_HORIZONTAL });
180e41f4b71Sopenharmony_ci                if (!isInitialRender) {
181e41f4b71Sopenharmony_ci                    Image.pop();
182e41f4b71Sopenharmony_ci                }
183e41f4b71Sopenharmony_ci                ViewStackProcessor.StopGetAccessRecording();
184e41f4b71Sopenharmony_ci            });
185e41f4b71Sopenharmony_ci            this.observeComponentCreation((elmtId, isInitialRender) => {
186e41f4b71Sopenharmony_ci                ViewStackProcessor.StartGetAccessRecordingFor(elmtId);
187e41f4b71Sopenharmony_ci                Text.create(this.appLabel);
188e41f4b71Sopenharmony_ci                Image.id("enhanceAppLabel");
189e41f4b71Sopenharmony_ci                Text.maxLines(1);
190e41f4b71Sopenharmony_ci                Text.fontSize(TITLE_TEXT_FONT_SIZE);
191e41f4b71Sopenharmony_ci                Text.fontColor(this.textColor);
192e41f4b71Sopenharmony_ci                Text.fontWeight(TITLE_TEXT_FONT_WEIGHT);
193e41f4b71Sopenharmony_ci                Text.textOverflow({ overflow: TextOverflow.Ellipsis });
194e41f4b71Sopenharmony_ci                Text.textAlign(TextAlign.Start);
195e41f4b71Sopenharmony_ci                Text.layoutWeight(1.0);
196e41f4b71Sopenharmony_ci                if (!isInitialRender) {
197e41f4b71Sopenharmony_ci                    Text.pop();
198e41f4b71Sopenharmony_ci                }
199e41f4b71Sopenharmony_ci                ViewStackProcessor.StopGetAccessRecording();
200e41f4b71Sopenharmony_ci            });
201e41f4b71Sopenharmony_ci            Text.pop();
202e41f4b71Sopenharmony_ci            Row.pop();
203e41f4b71Sopenharmony_ci        }
204e41f4b71Sopenharmony_ci        rerender() {
205e41f4b71Sopenharmony_ci            this.updateDirtyElements();
206e41f4b71Sopenharmony_ci        }
207e41f4b71Sopenharmony_ci    }
208e41f4b71Sopenharmony_ci    ViewStackProcessor.StartGetAccessRecordingFor(ViewStackProcessor.AllocateNewElmetIdForNextComponent());
209e41f4b71Sopenharmony_ci    // The loadDocument method needs to be changed to loadCustomTitleBar.
210e41f4b71Sopenharmony_ci    loadCustomTitleBar(new Index(undefined, {}));
211e41f4b71Sopenharmony_ci    ViewStackProcessor.StopGetAccessRecording();
212e41f4b71Sopenharmony_ci    ```
213e41f4b71Sopenharmony_ci    - In general cases, the .js or .ts (.ets) files generated in DevEco Studio are stored in the **build/default/cache/default/default@CompileArkTS/esmodule/debug/entry/src/main/ets/pages/** directory of the project.
214e41f4b71Sopenharmony_ci    - Process the .ets file so that it complies with the JS syntax specifications.
215e41f4b71Sopenharmony_ci    - The **loadDocument** or **registerNameRouter** method in the .js file must be be changed to **loadCustomTitleBar**.
216e41f4b71Sopenharmony_ci        ```js
217e41f4b71Sopenharmony_ci        loadCustomTitleBar(new Index(undefined, {}));
218e41f4b71Sopenharmony_ci        ```
219e41f4b71Sopenharmony_ci
220e41f4b71Sopenharmony_ci3. Use the **BUILD.gn** file to add the .js file as a system build dependency to generate an .abc file.
221e41f4b71Sopenharmony_ci    ```
222e41f4b71Sopenharmony_ci    import("//arkcompiler/ets_frontend/es2panda/es2abc_config.gni")
223e41f4b71Sopenharmony_ci
224e41f4b71Sopenharmony_ci    es2abc_gen_abc("gen_customtitle_abc") {
225e41f4b71Sopenharmony_ci    src_js = rebase_path("customtitle.js")
226e41f4b71Sopenharmony_ci    dst_file = rebase_path(target_out_dir + "/customtitle.abc")
227e41f4b71Sopenharmony_ci    in_puts = [ "customtitle.js" ]
228e41f4b71Sopenharmony_ci    out_puts = [ target_out_dir + "/customtitle.abc" ]
229e41f4b71Sopenharmony_ci    extra_args = [ "--module" ]
230e41f4b71Sopenharmony_ci    }
231e41f4b71Sopenharmony_ci    ```
232e41f4b71Sopenharmony_ci   Add **gen_customtitle_abc** to the build dependencies in the **foundation/arkui/ace_engine/frameworks/core/components_ng/pattern/BUILD.gn** file. The .abc file generated after compilation is stored in the **obj/foundation/arkui/ace_engine/frameworks/core/components_ng/pattern/container_modal/interfaces** directory.
233e41f4b71Sopenharmony_ci   
234e41f4b71Sopenharmony_ci   For details, see the implementation in [foundation/arkui/ace_engine/frameworks/core/components_ng/pattern/container_modal/interfaces](https://gitee.com/openharmony/arkui_ace_engine/tree/master/frameworks/core/components_ng/pattern/container_modal/interfaces).
235e41f4b71Sopenharmony_ci
236e41f4b71Sopenharmony_ci
237e41f4b71Sopenharmony_ci4. Add the .abc file to a specific directory in the system, for example, **/system/lib64/**, and run the following command to set **persist.sys.arkui.customtitle** to the path of the .abc file so that the custom title bar can be read and displayed properly.
238e41f4b71Sopenharmony_ci
239e41f4b71Sopenharmony_ci    ```
240e41f4b71Sopenharmony_ci    hdc shell param set persist.sys.arkui.customtitle /system/lib64/customtitle.abc
241e41f4b71Sopenharmony_ci    ```
242