1/** 2 * Copyright (c) 2021-2022 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 { 17 AppMenu, 18 AppIcon, 19 ScrollerComponent, 20} from '@ohos/common/component' 21import { 22 LauncherDragItemInfo, 23 DockItemInfo, 24 CommonConstants, 25 StyleConstants, 26 ResourceManager, 27 Log, 28 MenuInfo 29} from '@ohos/common'; 30import { SmartDockStyleConfig } from '../config/SmartDockStyleConfig'; 31 32let mSmartDockStyleConfig: SmartDockStyleConfig | null = null; 33const TAG = 'RecentLayout'; 34 35interface DockRecentPopup { 36 show: boolean; 37 showItem: string; 38 popup: CustomPopupOptions | null; 39} 40 41interface DockPadding { 42 right: number; 43 left: number; 44 top: number; 45 bottom: number; 46} 47 48@Component 49export default struct RecentLayout { 50 @StorageLink('sysUiRecentOnClickEvent') @Watch('sysUiRecentOnClick') sysUiRecentOnClickEvent: number = 0; 51 @StorageLink('dockPadding') dockPadding: DockPadding = {right: 0, left: 0, top: 0, bottom: 0}; 52 @State isHover: boolean = false; 53 @State showPopup: boolean = false; 54 @State onHoverItem: string = ''; 55 @Link @Watch('onDockListChange') appList: Array<DockItemInfo>; 56 mRecentMaxNum: number = 0; 57 @Link mSmartDockStyleConfig: SmartDockStyleConfig; 58 onItemClick: Function = () => {}; 59 buildMenu: (item: DockItemInfo) => MenuInfo[] = (item: DockItemInfo): MenuInfo[] => []; 60 onHoverEvent: Function = () => {}; 61 onDockListChangeFunc: Function = () => {}; 62 isScrollHover: boolean = false; 63 popup: DockRecentPopup = { show: false, showItem: '', popup: null }; 64 onClickWithPopup: boolean = false; 65 autoCancel: boolean = false; 66 private updateData: Function = () => {}; 67 68 aboutToAppear(): void { 69 mSmartDockStyleConfig = this.mSmartDockStyleConfig; 70 } 71 72 aboutToDisappear(): void { 73 this.isHover = false; 74 this.showPopup = false; 75 this.onHoverItem = ''; 76 this.isScrollHover = false; 77 this.onClickWithPopup = false; 78 this.autoCancel = false; 79 } 80 81 private sysUiRecentOnClick() { 82 this.showPopup = false; 83 this.popup = { show: false, showItem: '', popup: null }; 84 } 85 86 @Builder popupBuilder() { 87 Column() { 88 ScrollerComponent({ 89 popupHide: () => { 90 this.showPopup = false; 91 this.popup = { show: false, showItem: '', popup: null }; 92 }, 93 updateData: (show: boolean, bundleName: string, callback: () => void) => { 94 this.updateData = () => { 95 callback(); 96 setTimeout(() => { 97 this.onHoverEvent(true, bundleName); 98 }, 100) 99 } 100 if (show) { 101 this.updateData(); 102 this.getShowPopup(); 103 return; 104 } 105 this.showPopup = false; 106 this.popup = { show: false, showItem: '', popup: null }; 107 } 108 }) 109 }.onHover((isScrollHover: boolean) => { 110 this.autoCancel = false; 111 if (isScrollHover) { 112 this.isScrollHover = true; 113 } else { 114 this.isScrollHover = false; 115 } 116 this.getShowPopup(); 117 }) 118 .onClick(() => { 119 this.getShowPopup(); 120 }) 121 } 122 123 async getShowPopup() { 124 await this.delay(500); 125 if (this.popup.show || this.isScrollHover) { 126 this.showPopup = true; 127 } else { 128 this.showPopup = false; 129 } 130 return this.showPopup; 131 } 132 133 delay(ms: number) { 134 return new Promise<PromiseConstructor>(resolve => setTimeout(resolve, ms)); 135 } 136 137 build() { 138 List({ space: (mSmartDockStyleConfig?.mListItemGap as number) }) { 139 ForEach(this.appList, (item: DockItemInfo) => { 140 ListItem() { 141 AppItem({ 142 appInfo: item, 143 buildMenu: this.buildMenu 144 }) 145 } 146 .bindPopup(this.showPopup && item.bundleName == this.onHoverItem && !AppStorage.get('smartDockShowMenu') as boolean, { 147 builder: this.popupBuilder, 148 placement: Placement.Top, 149 enableArrow: true, 150 autoCancel: this.autoCancel, 151 maskColor: ('rgba(250,250,250,0)'), 152 popupColor: ('rgba(250,250,250,0.6)'), 153 onStateChange: (e) => { 154 if (!e.isVisible && this.autoCancel) { 155 this.popup = { show: false, showItem: '', popup: null }; 156 this.onHoverItem = ''; 157 this.onClickWithPopup = false; 158 this.autoCancel = false; 159 this.showPopup = false; 160 AppStorage.setOrCreate('snapshotList', []); 161 AppStorage.setOrCreate('recentShowPopup', false); 162 } 163 if (this.updateData) { 164 this.updateData(); 165 this.updateData = () => { 166 } 167 } 168 }, 169 }) 170 .onHover((isHover) => { 171 this.autoCancel = false; 172 if (this.onHoverEvent) { 173 this.onHoverEvent(isHover, item.bundleName); 174 this.onHoverItem = item.bundleName as string; 175 this.getShowPopup(); 176 } 177 }) 178 .onClick((event: ClickEvent) => { 179 this.onItemClick(event, item); 180 this.onClickWithPopup = AppStorage.get('recentShowPopup') as boolean; 181 Log.showInfo(TAG, `onClick this.onClickWithPopup: ${this.onClickWithPopup}`); 182 if (this.onClickWithPopup) { 183 this.autoCancel = true; 184 this.showPopup = true 185 this.onHoverItem = item.bundleName as string; 186 } 187 AppStorage.setOrCreate('recentShowPopup', false); 188 }) 189 }, (item: DockItemInfo) => JSON.stringify(item)) 190 } 191 .enableScrollInteraction(false) 192 .scrollBar(BarState.Off) 193 .padding(this.dockPadding) 194 .width(this.getListWidth()) 195 .height(this.mSmartDockStyleConfig?.mDockHeight as number) 196 .backgroundColor(this.mSmartDockStyleConfig?.mBackgroundColor as string) 197 .borderRadius(this.mSmartDockStyleConfig?.mDockRadius as number) 198 .backdropBlur(this.mSmartDockStyleConfig?.mBackdropBlur as number) 199 .visibility(this.getListWidth() === 0 ? Visibility.None : Visibility.Visible) 200 .listDirection(Axis[(this.mSmartDockStyleConfig?.mListDirection as string)]) 201 } 202 203 getListWidth(): number { 204 let mRecentMaxNum = this.mSmartDockStyleConfig?.mMaxRecentNum as number; 205 let width = 0; 206 if (AppStorage.get('deviceType') == CommonConstants.DEFAULT_DEVICE_TYPE) { 207 return width; 208 } 209 if (typeof this.appList === 'undefined' || this.appList == null || this.appList.length === 0) { 210 return width; 211 } 212 let num = this.appList.length; 213 if (num > mRecentMaxNum) { 214 num = mRecentMaxNum; 215 } 216 width = (this.mSmartDockStyleConfig?.mDockPadding as number) * 2 + 217 num * (this.mSmartDockStyleConfig?.mListItemWidth as number) + 218 (num - 1) * (this.mSmartDockStyleConfig?.mListItemGap as number); 219 return width; 220 } 221 222 private onDockListChange() { 223 this.onDockListChangeFunc(); 224 } 225} 226 227@Component 228struct AppItem { 229 @State isShow: boolean = false; 230 appInfo: DockItemInfo = new DockItemInfo(); 231 buildMenu: (item: DockItemInfo) => MenuInfo[] = (item: DockItemInfo): MenuInfo[] => []; 232 private menuInfo: MenuInfo[] = []; 233 234 aboutToAppear(): void { 235 this.menuInfo = this.buildMenu(this.appInfo); 236 } 237 238 aboutToDisappear(): void { 239 this.isShow = false; 240 } 241 242 private getLongPress(): boolean { 243 return AppStorage.get('isLongPress') as boolean; 244 } 245 246 @Builder MenuBuilder() { 247 Column() { 248 AppMenu({ 249 menuInfoList: this.menuInfo, 250 closeMenu: () => { 251 this.isShow = false; 252 } 253 }) 254 } 255 .alignItems(HorizontalAlign.Center) 256 .justifyContent(FlexAlign.Center) 257 .width(StyleConstants.CONTEXT_MENU_WIDTH) 258 .height(StyleConstants.DEFAULT_40 * this.menuInfo.length + StyleConstants.DEFAULT_8) 259 } 260 261 build() { 262 Column() { 263 Row() { 264 AppIcon({ 265 iconSize: mSmartDockStyleConfig?.mIconSize as number, 266 iconId: this.appInfo.appIconId as number, 267 bundleName: this.appInfo.bundleName as string, 268 moduleName: this.appInfo.moduleName as string, 269 icon: ResourceManager.getInstance().getCachedAppIcon(this.appInfo.appIconId as number, 270 this.appInfo.bundleName as string, this.appInfo.moduleName as string), 271 badgeNumber: this.appInfo.badgeNumber as number 272 }) 273 } 274 .width(mSmartDockStyleConfig?.mListItemWidth as number) 275 .height(mSmartDockStyleConfig?.mListItemHeight as number) 276 .backgroundColor(mSmartDockStyleConfig?.mItemBackgroundColor as string) 277 .borderRadius(mSmartDockStyleConfig?.mItemBorderRadius as number) 278 } 279 .gesture( 280 LongPressGesture({ repeat: false }) 281 .onAction((event: GestureEvent) => { 282 this.isShow = true; 283 AppStorage.setOrCreate('isLongPress', true); 284 }) 285 ) 286 .bindPopup(this.isShow, { 287 builder: this.MenuBuilder, 288 placement: Placement.Top, 289 popupColor: Color.White, 290 arrowOffset: 3 * ((mSmartDockStyleConfig?.mIconSize as number) / 2) + 291 (mSmartDockStyleConfig?.mListItemGap as number), 292 onStateChange: (e) => { 293 if (!e.isVisible) { 294 this.isShow = false; 295 } 296 AppStorage.setOrCreate('smartDockShowMenu', e.isVisible) 297 }, 298 autoCancel: true 299 }) 300 .onTouch((event: TouchEvent) => { 301 if (event.type == CommonConstants.TOUCH_TYPE_UP) { 302 AppStorage.setOrCreate('isLongPress', false); 303 } 304 const dragItemInfo: LauncherDragItemInfo = AppStorage.get<LauncherDragItemInfo>('dragItemInfo') as LauncherDragItemInfo; 305 if (dragItemInfo.isDragging) { 306 this.isShow = false; 307 } 308 }) 309 .onMouse((event: MouseEvent) => { 310 Log.showInfo(TAG, `onMouse MouseType: ${event.button}`); 311 if (event.button == MouseButton.Right) { 312 event.stopPropagation(); 313 AppStorage.setOrCreate('selectDesktopAppItem', ''); 314 this.isShow = true; 315 } 316 }) 317 } 318}