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} from '@ohos/common/component' 20import { 21 DockItemInfo, 22 LauncherDragItemInfo, 23 CommonConstants, 24 StyleConstants, 25 ResourceManager, 26 Log, 27 MenuInfo, 28 BadgeManager 29} from '@ohos/common'; 30import { SmartDockStyleConfig } from '../config/SmartDockStyleConfig'; 31import SmartDockDragHandler from '../common/SmartDockDragHandler'; 32 33let mSmartDockStyleConfig: SmartDockStyleConfig | null = null; 34const TAG = 'ResidentLayout'; 35 36interface DockPadding { 37 right: number; 38 left: number; 39 top: number; 40 bottom: number; 41} 42 43@Component 44export default struct ResidentLayout { 45 @StorageLink('dockPadding') dockPadding: DockPadding = { right: 0, left: 0, top: 0, bottom: 0 }; 46 @StorageLink('residentList') @Watch('onDockListChange') appList: Array<DockItemInfo> = []; 47 @Link mSmartDockStyleConfig: SmartDockStyleConfig; 48 onItemClick: (event: ClickEvent, item: DockItemInfo) => void = (event: ClickEvent, item: DockItemInfo) => {}; 49 buildMenu: (item: DockItemInfo) => MenuInfo[] = (item: DockItemInfo): MenuInfo[] => []; 50 onDockListChangeFunc: () => void = () => {}; 51 52 aboutToAppear(): void { 53 mSmartDockStyleConfig = this.mSmartDockStyleConfig; 54 this.onDockListChange(); 55 } 56 57 aboutToDisappear(): void { 58 } 59 60 getListWidth(): number { 61 let residentMaxNum = this.mSmartDockStyleConfig.mMaxDockNum; 62 let width = 0.0; 63 if (AppStorage.get('deviceType') == CommonConstants.DEFAULT_DEVICE_TYPE) { 64 Log.showDebug(TAG, `getListWidth mDockPadding: ${this.mSmartDockStyleConfig.mDockPadding}, mMaxNum: ${residentMaxNum}`); 65 width = this.mSmartDockStyleConfig.mDockPadding * 2 + residentMaxNum * 66 (this.mSmartDockStyleConfig.mListItemWidth) + (residentMaxNum - 1) * (this.mSmartDockStyleConfig.mListItemGap); 67 Log.showDebug(TAG, `getListWidth width: ${width}`); 68 } else { 69 if (this.appList == null || this.appList.length === 0) { 70 } else { 71 let num = this.appList.length; 72 if (num > residentMaxNum) { 73 num = residentMaxNum; 74 } 75 width = this.mSmartDockStyleConfig.mDockPadding * 2 + num * 76 (this.mSmartDockStyleConfig.mListItemWidth) + (num - 1) * (this.mSmartDockStyleConfig.mListItemGap); 77 } 78 } 79 return width; 80 } 81 82 private onDockListChange() { 83 this.onDockListChangeFunc(); 84 } 85 86 build() { 87 if (this.getListWidth && this.getListWidth() !== 0) { 88 Row() { 89 List({ space: this.appList.length == 0 ? 0 : this.mSmartDockStyleConfig.mListItemGap }) { 90 ForEach(this.appList, (item: DockItemInfo) => { 91 ListItem() { 92 AppItem({ 93 appInfo: item, 94 buildMenu: this.buildMenu, 95 onItemClick: this.onItemClick 96 }) 97 } 98 }, (item: DockItemInfo) => JSON.stringify(item)) 99 } 100 .enableScrollInteraction(false) 101 .scrollBar(BarState.Off) 102 .height('100%') 103 .animation({ 104 curve: Curve.Friction 105 }) 106 .listDirection(Axis[this.mSmartDockStyleConfig.mListDirection]) 107 } 108 .backgroundColor(this.mSmartDockStyleConfig.mBackgroundColor) 109 .borderRadius(this.mSmartDockStyleConfig.mDockRadius) 110 .backdropBlur(this.mSmartDockStyleConfig.mBackdropBlur) 111 .padding(this.appList.length == 0 ? this.mSmartDockStyleConfig.mDockPadding : this.dockPadding) 112 .width(this.getListWidth()) 113 .height(this.mSmartDockStyleConfig.mDockHeight) 114 .justifyContent(FlexAlign.Center) 115 .onDragEnter((event: DragEvent, extraParams: string) => { 116 Log.showDebug(TAG, `onDragEnter extraParams: ${extraParams}, event: [${event.getWindowX()}, ${event.getWindowY()}]`); 117 }) 118 .onDragMove((event: DragEvent, extraParams: string) => { 119 Log.showDebug(TAG, `onDragMove event: [${event.getWindowX()}, ${event.getWindowY()}]`); 120 }) 121 .onDragLeave((event: DragEvent, extraParams: string) => { 122 Log.showDebug(TAG, `onDragLeave event: [${event.getWindowX()}, ${event.getWindowY()}]`); 123 }) 124 .onDrop((event: DragEvent, extraParams: string) => { 125 Log.showInfo(TAG, `onDrop event: [${event.getWindowX()}, ${event.getWindowY()}]`); 126 const dragResult = SmartDockDragHandler.getInstance().onDragDrop(event.getWindowX(), event.getWindowY()); 127 AppStorage.setOrCreate('selectAppIndex', null); 128 const dragItemInfo: LauncherDragItemInfo = AppStorage.get('dragItemInfo') as LauncherDragItemInfo; 129 if (dragItemInfo.bundleName) { 130 BadgeManager.getInstance().updateBadgeNumber(dragItemInfo.bundleName, dragItemInfo.badgeNumber); 131 } 132 if (!dragResult) { 133 AppStorage.setOrCreate<LauncherDragItemInfo>('dragItemInfo', new LauncherDragItemInfo()); 134 } else { 135 // Wait for the UI rendering to end. 136 setTimeout(() => { 137 AppStorage.setOrCreate<LauncherDragItemInfo>('dragItemInfo', new LauncherDragItemInfo()); 138 }, 50); 139 } 140 }) 141 } 142 } 143} 144 145@Component 146struct AppItem { 147 @StorageLink('dragItemInfo') smartDragItemInfo: LauncherDragItemInfo = new LauncherDragItemInfo(); 148 @StorageLink('dragItemType') dragItemType: number = CommonConstants.DRAG_FROM_DOCK; 149 @State isShow: boolean = false; 150 onItemClick: (event: ClickEvent, item: DockItemInfo) => void = (event: ClickEvent, item: DockItemInfo) => {}; 151 appInfo: DockItemInfo = new DockItemInfo(); 152 buildMenu: (item: DockItemInfo) => MenuInfo[] = (item: DockItemInfo): MenuInfo[] => []; 153 private menuInfo: MenuInfo[] = []; 154 155 aboutToAppear(): void { 156 this.menuInfo = this.buildMenu(this.appInfo); 157 } 158 159 aboutToDisappear(): void { 160 this.isShow = false; 161 this.menuInfo = []; 162 } 163 164 private getLongPress(): boolean { 165 return AppStorage.get('isLongPress') as boolean; 166 } 167 168 @Builder MenuBuilder() { 169 Column() { 170 AppMenu({ 171 menuInfoList: this.menuInfo, 172 closeMenu: () => { 173 this.isShow = false; 174 } 175 }) 176 } 177 .alignItems(HorizontalAlign.Center) 178 .justifyContent(FlexAlign.Center) 179 .width(StyleConstants.CONTEXT_MENU_WIDTH) 180 .height(StyleConstants.DEFAULT_40 * this.menuInfo.length + StyleConstants.DEFAULT_8) 181 } 182 183 build() { 184 Column() { 185 AppIcon({ 186 iconSize: mSmartDockStyleConfig?.mIconSize as number, 187 iconId: this.appInfo.appIconId, 188 bundleName: this.appInfo.bundleName, 189 moduleName: this.appInfo.moduleName, 190 icon: ResourceManager.getInstance().getCachedAppIcon(this.appInfo.appIconId, 191 this.appInfo.bundleName, this.appInfo.moduleName), 192 badgeNumber: this.appInfo.badgeNumber 193 }) 194 } 195 .visibility(this.dragItemType === CommonConstants.DRAG_FROM_DOCK && 196 this.smartDragItemInfo.keyName === this.appInfo.keyName ? 197 Visibility.Hidden : Visibility.Visible) 198 .width(mSmartDockStyleConfig?.mListItemWidth as number) 199 .height(mSmartDockStyleConfig?.mListItemHeight as number) 200 .backgroundColor(mSmartDockStyleConfig?.mItemBackgroundColor as string) 201 .borderRadius(mSmartDockStyleConfig?.mItemBorderRadius as number) 202 .parallelGesture( 203 LongPressGesture({ repeat: false }) 204 .onAction((event: GestureEvent) => { 205 Log.showInfo(TAG, 'onAction start'); 206 this.isShow = true; 207 AppStorage.setOrCreate('isLongPress', true); 208 }) 209 ) 210 .bindPopup(this.isShow, { 211 builder: this.MenuBuilder, 212 placement: Placement.Top, 213 popupColor: Color.White, 214 arrowOffset: AppStorage.get('deviceType') == CommonConstants.DEFAULT_DEVICE_TYPE 215 ? null : 3 * ((mSmartDockStyleConfig?.mIconSize as number) / 2) + 216 (mSmartDockStyleConfig?.mListItemGap as number), // Avoid the popup offset problem in phone form 217 onStateChange: (e) => { 218 if (!e.isVisible) { 219 this.isShow = false; 220 } 221 AppStorage.setOrCreate('smartDockShowMenu', e.isVisible) 222 }, 223 autoCancel: true 224 }) 225 .onTouch((event: TouchEvent) => { 226 Log.showInfo(TAG, `onTouch event type: ${event.type}`); 227 if (event.type === CommonConstants.TOUCH_TYPE_UP && this.smartDragItemInfo.isDragging) { 228 let mIsDragEffectArea = 229 SmartDockDragHandler.getInstance().isDragEffectArea(event.touches[0].windowX, event.touches[0].windowY); 230 if (!mIsDragEffectArea) { 231 AppStorage.setOrCreate<LauncherDragItemInfo>('dragItemInfo', new LauncherDragItemInfo()); 232 AppStorage.setOrCreate('selectAppIndex', null); 233 } 234 AppStorage.setOrCreate('isLongPress', false); 235 } 236 if (this.smartDragItemInfo.isDragging) { 237 this.isShow = false; 238 } 239 }) 240 .onClick((event) => { 241 this.onItemClick(event, this.appInfo); 242 }) 243 .onMouse((event: MouseEvent) => { 244 Log.showInfo(TAG, `onMouse MouseType: ${event.button}`); 245 if (event.button == MouseButton.Right) { 246 event.stopPropagation(); 247 AppStorage.setOrCreate('selectDesktopAppItem', ''); 248 this.isShow = true; 249 } 250 }) 251 .onDragStart((event: DragEvent, extraParams: string) => { 252 Log.showInfo(TAG, 'DragStart'); 253 this.dragItemType = CommonConstants.DRAG_FROM_DOCK; 254 this.appInfo.isDragging = true; 255 this.smartDragItemInfo = this.appInfo as LauncherDragItemInfo; 256 Log.showInfo(TAG, `smartDragItemInfo: ${JSON.stringify(this.smartDragItemInfo)}`); 257 const selectAppIndex = 258 SmartDockDragHandler.getInstance().getDragItemIndexByCoordinates(event.getWindowX(), event.getWindowY()); 259 AppStorage.setOrCreate('selectAppIndex', selectAppIndex); 260 }) 261 .onDragEnd((event: DragEvent, extraParams: string) => { 262 Log.showInfo(TAG, `onDragEnd event: [${event.getWindowX()}, ${event.getWindowY()}]` + event.getResult()); 263 AppStorage.setOrCreate<LauncherDragItemInfo>('dragItemInfo', new LauncherDragItemInfo()); 264 }) 265 } 266}