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