1/* 2 * Copyright (c) 2023-2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import { KeyCode } from '@ohos.multimodalInput.keyCode' 17 18export enum EditableLeftIconType { 19 Back, 20 Cancel 21} 22 23export declare type EditableTitleBarMenuItem = { 24 value: ResourceStr 25 isEnabled: boolean 26 action?: () => void 27} 28 29const PUBLIC_CANCEL = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMAAAADACAYAAABS3Gw' + 30 'HAAAAIGNIUk0AAHomAACAhAAA+gAAAIDoAAB1MAAA6mAAADqYAAAXcJy6UTwAAAAEZ0FNQQAAsY58+1GTAAAAAXNSR0IA' + 31 'rs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAOxAAADsQBlSsOGwAABKpJREFUeNrt3bFrlHccx/Ff4o2dgkOQ4' + 32 'JzR4SYJgksnbYcGOrTQsUPtpE4d/RNcFBeFlg7NUlTSTrXQQgsKGV0KHTs4ODiJSL8PdxaUGJK7pPc893m94TvleZLnnt' + 33 '/7fcc9z5FrDQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnDQrPTuej2u2a87VvKp5XLNT87OlGiSf1lx' + 34 '6Zz2/q3kkgLdZr7k5lX8/7tZ8XfOCU4OgW887U/n341bN9T6s56menLAfaj464OfdM8iF6XYv+dV7+X+pOX/ANuOatZqH' + 35 'Amjti5prh9jurAgGI//mIbbtIvi15u9FHvBqD07a50fYdqtmt+YDrg1a/jd8tuiD7kMA4yNuL4LlkH+WtV/KAEYz7COC4' + 36 'cs/69ovXQB7M+4ngmHLP8/aL1UAD+bYVwTDlX/etT8W+nAV6M82uQS6PuP+rg4NV/5vBNDa6za5HLY9xzO5CIYl/9OaT5' + 37 'obYf/xrE1uioggQ/6LNf/04QGd6tHJFQH5owMQAfnjAxAB+eMDEAH54wMQAfnjAxAB+eMDEAH54wMQAfnjAxAB+eMDEAH' + 38 '54wMQAfnjAxAB+eMDEAH54wMQAfnjAxAB+eMDEAH54wMQAfnjAxAB+eMDEAH54wMQAfnjAxAB+eMDSI6A/AKIjYD8AoiN' + 39 'gPwCiI2A/AKIjYD8AoiNgPwCiI2A/AKIjYD8AoiNgPwCiI2A/AKIjYD8AoiNgPwCiI2A/AKIjYD8AoiNgPwCiI2A/AKIj' + 40 'YD8AoiNgPwCiI2A/AKIjYD8AoiNgPwCiI1gjfyLY8UpmJnNqbjrc/yO32pOk98rQPIrwWnyCyA5AvILQATkF4AIyC8AEZ' + 41 'BfACIgvwBEQH4BiID8J8qqU3BiPJ8O+XuMO8Eng8/2CID85BcA+ckvAPKT35tg8h+n/KP2/3/2SADojfzf1+yKYH7cBxi' + 42 'm/N39hWX8RnsBkP9Q8r9BBAKIlV8EAoiXXwQCiJdfBAKIl18EAoiXXwQz4D5A/+Tv2KjZmuPvdfu6T+AVYJDyd3Qfo17G' + 43 'b7QXAPkPzTMRCCBVfhEIIF5+EQggXn4RCCBefhEIIF5+EQggXn4RCCBefhEIIF5+EQggXn4RCCBefhEIIF5+EQggXn4RC' + 44 'CBefhEIIF5+EQggXn4RCCBefhEIwD+qFUFuAOQXQWwA5BdBbADkF0FsAOQXQWwA5BdBbADkF0FsAOQXQWwA5BdBbADkF0' + 45 'FsAOQXQWwA5BdBbADkF0FsAOQXQWwA5BdBbADkF0FsAOQXQWwA5BdBbADkF0FsAOQXQWwA5BdBdAD3a8bkj4rgTM2PAmj' + 46 'ty5or5I+L4FzNHzV/LfKB9OGb4rfJP0iO49xvL/pB9CGAMfljIxgLoLUR+WMjGAmgtT3yx0awJ4DWdsgfG8HOog+6D1eB' + 47 'ntR8WLNB/sFzlKtDnfw3BNDa65rfp2+I3hfBo5rL5B9UBFttcoNzP35qk8vfLxZ9sCs9OnHdG6Kvps8e3TXiVzWPp88Ut' + 48 '3k1OLr1vFpz6Z31/LbmntMDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAO/wLvsR65mx80NAAAAABJRU' + 49 '5ErkJggg==' 50 51const PUBLIC_OK = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMAAAADACAYAAABS3' + 52 'GwHAAAAIGNIUk0AAHomAACAhAAA+gAAAIDoAAB1MAAA6mAAADqYAAAXcJy6UTwAAAAEZ0FNQQAAsY58+1GTAAAAAXNSR0IArs4c6QAAAAZ' + 53 'iS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAOxAAADsQBlSsOGwAAA+lJREFUeNrt3bFrFgccx+GLlSDi4JDBITiJZHBwEBGRIoqKoIu6iVMd3' + 54 'OosCg6W0sm/wEFUDDgpCDoIDoqOKqIoHUrp4CDFoUMRB39HLotoeXMpMXff54EfFE0ivv1+kpQGrmkAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 55 'AAAAAAAAAAAAAAIBJTHkJGIhddT/V7ajbUPey7l7dlbpPAmCs1tb9Wne2++cvPa07Vfd7nw/+g9eXVT7+m3Wn69Z8421m6w7WXa37KADGN' + 56 'v4TE7ztTN36uvtL/UPWeJ0Z+PgXnejzBwmAMYx/8VuhTQIgcfy9CYCxjP9D3TsBkPqZf95XAFLH3372vyAAEsf/T93Ruvd93tn/CWbo4z9' + 57 'c96jvBxAAseMXANHjFwDR4xcA0eMXANHjFwDR4xcA0eMXANHjFwDR4xcA0eMXANHjFwDR4xcA0eMXANHjFwDR4xcA0eMXANHjFwDR4xcA0' + 58 'eMXANHjFwDR4xcA0eMXANHjFwDR4xcA0eMXANHjFwDR4xcA0eP/HgG0z3f9uVl45uu2ZuGBBu3zXn9rej7mEuMfSgDtA46v1c195ff+rbt' + 59 'U94stGv9KWqnHpLaPsXxSt/k//iXsq9vY9HjUJca/2gNoH2e/c4K32yUC4x9bAOvqrjeTP41GBMY/qgC2151Z4vuIwPhXxEo8I2xdz/c7W' + 60 '3fZXo1/6F8B/q471/N9fSUw/sEH8LEb8hYRGH9iAK3HdafrpkVg/IkBtA8yflt3bBn/3SEC4x9sAK3Xda9EYPypAYjA+OMDEIHxxwcgAuO' + 61 'PD0AExh8fgAiMPz4AERh/fAAiMP74AERg/PEBiMD44wMQgfHHByAC448PQATGHx+ACIw/PgARGH98AMkRGL8AYiMwfgHERmD8AoiNwPgFE' + 62 'BuB8QsgNgLjF0BsBMYvgNgIjF8AsREYvwBiIzB+AcRGYPwCiI3A+AUQG4HxCyA2AuMXQGwExi+A2AiMXwCxERi/AGIjMH4BjDaCTXUP6j5' + 63 '9423aJ2PeMH4BjDWCHd24n9f9+cXvHa+7U7fb+Fe/qeC/ezvU+e5bleX40A11pm6u+xapMX5fARK+Eix+u7O1brbp/1Bw4xfAoCP4Pxi/A' + 64 'GIjMH4BxEZg/AKIjcD4BRAbgfELIDYC4xdAbATGL4DYCIxfAIOL4EXdkbrpZX6sd3WH6p56WQUwJG/qbtX92Cz8EFwfD+sO1L31cq4uU16' + 65 'CibU/M3Sy7nzdlgnf51ndxbrbXj4BjCmEPXX76/Y2Cz8DNNP9+l91f3Sf8e92AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 66 'AAAAAzKZwK1uX4kZ6mnAAAAAElFTkSuQmCC' 67 68const PUBLIC_BACK = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMAAAADACAY' + 69 'AAABS3GwHAAAAIGNIUk0AAHomAACAhAAA+gAAAIDoAAB1MAAA6mAAADqYAAAXcJy6UTwAAAAEZ0FNQQAAsY58+1GTAAAAA' + 70 'XNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAOxAAADsQBlSsOGwAAA8VJREFUeNrt3LFLlHEYwPFXz0G' + 71 'iIZpEoikkwsFRIiK3gqCigxIC/4Kmhv6OoChouaGoqKCgCKducGh0cDAIamhwiCaHCIeelztpUszee/vl8/nAM3Vd8nufr' + 72 '+fddVYVAAAAAAAAAAAAAAAAAAAAAABQijFH0KhrMd2Y2ZitmNWYRzHLjkYAB9lUzMOYizv8eS/mZsymoypLxxE0svzvY07' + 73 'vcpu5mOmY145LAAdx+U/u4bZzwx+JPjq2cow7glaWf1vXsQkg6/JvPwoggJTLjwDSL/8nRyiAzN/5nzpGAWRd/n7MM0cpg' + 74 'IzLvx6z6CjL453gdpZ/IWbDcQrA8iMAy48ALD8CsPwIwPIjAMuPACw/ArD8CMDyIwDLjwAsPwKw/AjA8iMAy48ALD8CsPw' + 75 'IwPIjAMuPACw/ArD85A3A8pM2AMtP2gAsP2kDsPykDcDykzYAy0/aACw/aQOw/KQNwPKTNgDLT9oALD9pA7D8pA3A8pM2A' + 76 'MtP2gAsP2kDsPykDcDykzYAy0/aACw/aQOw/KQNwPKTNgDLT9oALD9pA7D8pA3A8pM2AMtP2gAsP2kDsPykDcDykzYAy0/' + 77 'aACw/aQOw/KQNwPLz3xlv6H4mYp5YfrI+AizF9BwnI/AlZi3mbsxy03feaeh+HsQcc60YgSMxMzE3YmZj3sX8LOlHoPoLn' + 78 'HedaEE35n5pzwF856dN9SPBpZICmHRNaNnlkgL46nrQsvmSAqhftlx1TWjR4ZICqPVcE1q0XloA96rBa7XQhl5pAWzFXKm' + 79 '8i8vo9WMeN3VnnQa/sO8xL2POxEy7Toxo+RdjNpu6w1F9HuBqNXi99lw1eKMM9utHzIeYV8MftbccCQAAAAAAsBdt/XLc+s' + 80 'Py9W+MmPqL+1iJuVA1+C4gdFr6d77FvK0GH2nb739lPR5zNuZ51eBnQhFAJQIEIAIEIAIEIAIEIAIEIAIEIAIEIAIEIAIE' + 81 'IAIEIAIEIAIEIAIEIAIEIAIEIAIEIAIEIAIEIAIEIAIEIAIEIAIEIAIEIAIEIAIEIAIEIAIEIAIEIAIEIAIEIAIEIAIEIAI' + 82 'EIAIEIAIEIAIEIAIEIAIEIAIEIAIEIAIEIAIEIAIEIAIEIAIEIAIE8M8jmBlGgABSRnAqZiXms+MUQNYIDnkUKMu4I/gj6z' + 83 'ELMRv7/PsnHKEAMkcw6fgEkDmCNUcngMwRvHFsngRnfWJcL/9tRyaAgxrB+ZijO9ymH7MUs+m4yjLmCBozEXMr5nr1+9We1' + 84 'ZgXMXccDwAAAAAAAAAAAAAAAAAAAAAAwO5+AfVgtqHKRnawAAAAAElFTkSuQmCC' 85 86@Component 87export struct EditableTitleBar { 88 leftIconStyle: EditableLeftIconType 89 title: ResourceStr 90 menuItems: Array<EditableTitleBarMenuItem> 91 onSave?: () => void 92 onCancel?: () => void 93 94 @State titleMaxWidth: number = 0 95 @State backActive: boolean = false 96 97 static readonly maxCountOfExtraItems = 2 98 private static readonly totalHeight = 56 99 private static readonly leftPadding = 12 100 private static readonly rightPadding = 12 101 private static readonly titlePadding = 16 102 103 build() { 104 Flex({ 105 justifyContent: FlexAlign.SpaceBetween, 106 alignItems: ItemAlign.Stretch 107 }) { 108 Row() { 109 if (this.leftIconStyle == EditableLeftIconType.Back) { 110 Navigator() 111 .active(this.backActive) 112 113 ImageMenuItem({ item: { 114 value: PUBLIC_BACK, 115 isEnabled: true, 116 action: () => this.backActive = true 117 } }) 118 } else { 119 ImageMenuItem({ item: { 120 value: PUBLIC_CANCEL, 121 isEnabled: true, 122 action: () => this.onCancel && this.onCancel() 123 } }) 124 } 125 126 Column() { 127 Text(this.title) 128 .fontWeight(FontWeight.Medium) 129 .fontSize($r('sys.float.ohos_id_text_size_headline8')) 130 .fontColor($r('sys.color.ohos_id_color_titlebar_text')) 131 .maxLines(1) 132 .textOverflow({ overflow: TextOverflow.Ellipsis }) 133 .constraintSize({ maxWidth: this.titleMaxWidth }) 134 } 135 .justifyContent(FlexAlign.Start) 136 .alignItems(HorizontalAlign.Start) 137 .constraintSize({ maxWidth: this.titleMaxWidth }) 138 } 139 .margin({ left: $r('sys.float.ohos_id_default_padding_start') }) 140 141 EditableTitleBarMenuSection({ 142 menuItems: this.menuItems, 143 onSave: this.onSave 144 }) 145 } 146 .width('100%') 147 .height(EditableTitleBar.totalHeight) 148 .backgroundColor($r('sys.color.ohos_id_color_background')) 149 .onAreaChange((_oldValue: Area, newValue: Area) => { 150 let nValue = Number(newValue.width) 151 nValue = nValue - EditableTitleBar.leftPadding - EditableTitleBar.rightPadding - EditableTitleBar.titlePadding 152 nValue = nValue - ImageMenuItem.imageHotZoneWidth - ImageMenuItem.imageHotZoneWidth 153 if (this.menuItems === undefined) { 154 this.titleMaxWidth = nValue 155 return 156 } 157 if (this.menuItems.length > EditableTitleBar.maxCountOfExtraItems) { 158 this.titleMaxWidth = nValue - ImageMenuItem.imageHotZoneWidth * EditableTitleBar.maxCountOfExtraItems 159 } else { 160 this.titleMaxWidth = nValue - ImageMenuItem.imageHotZoneWidth * this.menuItems.length 161 } 162 }) 163 } 164} 165 166@Component 167struct EditableTitleBarMenuSection { 168 menuItems: Array<EditableTitleBarMenuItem> 169 onSave?: () => void 170 171 build() { 172 Column() { 173 Row() { 174 if (this.menuItems !== undefined && this.menuItems.length > 0) { 175 ForEach(this.menuItems.slice(0, EditableTitleBar.maxCountOfExtraItems), (item: EditableTitleBarMenuItem) => { 176 ImageMenuItem({ item: item }) 177 }) 178 } 179 ImageMenuItem({ item: { 180 value: PUBLIC_OK, 181 isEnabled: true, 182 action: () => this.onSave && this.onSave() 183 } }) 184 } 185 } 186 .margin({ 187 left: $r('sys.float.ohos_id_elements_margin_vertical_l'), 188 right: $r('sys.float.ohos_id_default_padding_end') 189 }) 190 .justifyContent(FlexAlign.Center) 191 } 192} 193 194@Component 195struct ImageMenuItem { 196 item: EditableTitleBarMenuItem 197 198 static readonly imageSize = 24 199 static readonly imageHotZoneWidth = 48 200 static readonly buttonBorderRadius = 8 201 static readonly focusBorderWidth = 2 202 static readonly disabledImageOpacity = 0.4 203 204 @State isOnFocus: boolean = false 205 @State isOnHover: boolean = false 206 @State isOnClick: boolean = false 207 208 getFgColor() { 209 return this.isOnClick 210 ? $r('sys.color.ohos_id_color_titlebar_icon_pressed') 211 : $r('sys.color.ohos_id_color_titlebar_icon') 212 } 213 214 getBgColor() { 215 if (this.isOnClick) { 216 return $r('sys.color.ohos_id_color_click_effect') 217 } else if (this.isOnHover) { 218 return $r('sys.color.ohos_id_color_hover') 219 } else { 220 return Color.Transparent 221 } 222 } 223 224 build() { 225 Row() { 226 Image(this.item.value) 227 .width(ImageMenuItem.imageSize) 228 .height(ImageMenuItem.imageSize) 229 .focusable(this.item.isEnabled) 230 } 231 .width(ImageMenuItem.imageHotZoneWidth) 232 .height(ImageMenuItem.imageHotZoneWidth) 233 .borderRadius(ImageMenuItem.buttonBorderRadius) 234 .foregroundColor(this.getFgColor()) 235 .backgroundColor(this.getBgColor()) 236 .justifyContent(FlexAlign.Center) 237 .opacity(this.item.isEnabled ? 1 : ImageMenuItem.disabledImageOpacity) 238 .stateStyles({ 239 focused: { 240 .border({ 241 radius: $r('sys.float.ohos_id_corner_radius_clicked'), 242 width: ImageMenuItem.focusBorderWidth, 243 color: $r('sys.color.ohos_id_color_focused_outline'), 244 style: BorderStyle.Solid 245 }) 246 }, 247 normal: { 248 .border({ 249 radius: $r('sys.float.ohos_id_corner_radius_clicked'), 250 width: 0 251 }) 252 } 253 }) 254 .onFocus(() => { 255 if (!this.item.isEnabled) { 256 return 257 } 258 this.isOnFocus = true 259 }) 260 .onBlur(() => this.isOnFocus = false) 261 .onHover((isOn) => { 262 if (!this.item.isEnabled) { 263 return 264 } 265 this.isOnHover = isOn 266 }) 267 .onKeyEvent((event) => { 268 if (!this.item.isEnabled) { 269 return 270 } 271 if (event.keyCode !== KeyCode.KEYCODE_ENTER && event.keyCode !== KeyCode.KEYCODE_SPACE) { 272 return 273 } 274 if (event.type === KeyType.Down) { 275 this.isOnClick = true 276 } 277 if (event.type === KeyType.Up) { 278 this.isOnClick = false 279 } 280 }) 281 .onTouch((event) => { 282 if (!this.item.isEnabled) { 283 return 284 } 285 if (event.type === TouchType.Down) { 286 this.isOnClick = true 287 } 288 if (event.type === TouchType.Up) { 289 this.isOnClick = false 290 } 291 }) 292 .onClick(() => this.item.isEnabled && this.item.action && this.item.action()) 293 } 294} 295 296export default { EditableLeftIconType, EditableTitleBar }