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