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