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