From a416923b1298c818ed9c991d6b439c5eea30a653 Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Sat, 23 May 2026 03:10:18 +0800
Subject: [PATCH] Archive QA-passed 7.6.0 window logic build

---
 Apptag/AppGridItem.swift |  335 -------------------------------------------------------
 1 files changed, 2 insertions(+), 333 deletions(-)

diff --git a/Apptag/AppGridItem.swift b/Apptag/AppGridItem.swift
index c187acf..5b39626 100644
--- a/Apptag/AppGridItem.swift
+++ b/Apptag/AppGridItem.swift
@@ -1,30 +1,11 @@
 import SwiftUI
 import AppKit
 
-// MARK: - App Grid Item (icon + name card, with Dock-like hover)
+// MARK: - App Grid Shared Types
 
-struct AppGridItem: View {
-    let app: AppInfo
-    let iconSize: CGFloat
-    var showName: Bool = true
-    var sourceTag: String? = nil
-    var dragModeActive: Bool = false
-    var onDragModeChange: ((Bool) -> Void)? = nil
-    var onBubbleHover: ((AppInfo, CGRect, AppBubbleHoverEvent) -> Void)? = nil
-    var onEditNote: ((AppInfo, CGRect) -> Void)? = nil
-    var bubbleDisabled: Bool = false
-    var showUncommonAppBubbles: Bool = AppDefaults.showUncommonAppBubbles
-    var itemID: String
-    var dragResetToken: Int = 0
-    let onSelect: () -> Void
-
-    @State private var interactionFrame: CGRect = .zero
-    @State private var isHovered = false
-
+enum AppGridItemMetrics {
     static let hoverScale: CGFloat = 1.22
     static let labelHeight: CGFloat = 14
-    private static let hoverInAnimation = Animation.easeOut(duration: 0.07)
-    private static let hoverOutAnimation = Animation.easeOut(duration: 0.045)
 
     static func stableWidth(iconSize: CGFloat) -> CGFloat {
         iconSize * hoverScale + 8
@@ -33,166 +14,11 @@
     static func stableHeight(iconSize: CGFloat) -> CGFloat {
         iconSize * hoverScale + labelHeight + 22
     }
-
-    private var iconSlotSize: CGFloat { iconSize * Self.hoverScale }
-    private var labelWidth: CGFloat { iconSize + 20 }
-    private var labelHeight: CGFloat { Self.labelHeight }
-
-    var body: some View {
-        VStack(spacing: 6) {
-            ZStack {
-                DraggableAppIconView(
-                    icon: app.icon,
-                    iconSize: iconSize,
-                    payload: "\(app.path.path)\n\(sourceTag ?? "")",
-                    resetToken: dragResetToken,
-                    onLongPress: { onDragModeChange?(true) },
-                    onDragEnd: { onDragModeChange?(false) },
-                    onClick: onSelect
-                )
-                .frame(width: iconSize, height: iconSize)
-                .scaleEffect(isHovered ? Self.hoverScale : 1.0)
-                .shadow(
-                    color: .black.opacity(isHovered ? 0.35 : 0),
-                    radius: isHovered ? 14 : 0,
-                    y: isHovered ? 8 : 0
-                )
-            }
-            .frame(width: iconSlotSize, height: iconSlotSize)
-
-            Text(app.name)
-                .font(.system(size: 11, weight: .medium))
-                .lineLimit(1)
-                .truncationMode(.tail)
-                .frame(width: labelWidth, height: labelHeight)
-                .opacity(showName ? 1 : (shouldShowHoverName ? 0.85 : 0))
-        }
-        .padding(.vertical, 8)
-        .padding(.horizontal, 4)
-        .frame(width: Self.stableWidth(iconSize: iconSize), height: Self.stableHeight(iconSize: iconSize))
-        .background(
-            AppHoverTrackingView { hovering, frame in
-                interactionFrame = frame
-                if bubbleDisabled || dragModeActive {
-                    setHoverState(false)
-                    onBubbleHover?(app, frame, .exited)
-                    return
-                }
-                if hovering {
-                    setHoverState(true)
-                    onBubbleHover?(app, frame, .entered(canShowBubble: shouldShowAppBubble))
-                } else if isHovered {
-                    setHoverState(false)
-                    onBubbleHover?(app, frame, .exited)
-                } else {
-                    setHoverState(false)
-                }
-            }
-        )
-        .contentShape(Rectangle())
-        .contextMenu {
-            Button(tr("appNote.edit")) {
-                onEditNote?(app, interactionFrame)
-            }
-        }
-        .opacity(dragModeActive ? 0.92 : 1)
-        .animation(.easeOut(duration: 0.08), value: dragModeActive)
-        .onChange(of: dragModeActive) { _, active in
-            if active {
-                setHoverState(false)
-                onBubbleHover?(app, interactionFrame, .exited)
-            }
-        }
-        .onChange(of: bubbleDisabled) { _, disabled in
-            if disabled {
-                setHoverState(false)
-                onBubbleHover?(app, interactionFrame, .exited)
-            }
-        }
-        .onDisappear {
-            if isHovered {
-                isHovered = false
-                onBubbleHover?(app, interactionFrame, .exited)
-            }
-        }
-    }
-
-    private var shouldShowAppBubble: Bool {
-        !showUncommonAppBubbles || app.isUncommon
-    }
-
-    private var shouldShowHoverName: Bool {
-        !showName && !shouldShowAppBubble && isHovered
-    }
-
-    private func setHoverState(_ hovering: Bool) {
-        guard isHovered != hovering else { return }
-        withAnimation(hovering ? Self.hoverInAnimation : Self.hoverOutAnimation) {
-            isHovered = hovering
-        }
-    }
 }
 
 enum AppBubbleHoverEvent {
     case entered(canShowBubble: Bool)
     case exited
-}
-
-struct AppHoverTrackingView: NSViewRepresentable {
-    let onHover: (Bool, CGRect) -> Void
-
-    func makeNSView(context: Context) -> AppHoverTrackingNSView {
-        let view = AppHoverTrackingNSView()
-        view.onHover = onHover
-        return view
-    }
-
-    func updateNSView(_ view: AppHoverTrackingNSView, context: Context) {
-        view.onHover = onHover
-    }
-}
-
-final class AppHoverTrackingNSView: NSView {
-    var onHover: ((Bool, CGRect) -> Void)?
-
-    override func updateTrackingAreas() {
-        super.updateTrackingAreas()
-        trackingAreas.forEach(removeTrackingArea)
-        addTrackingArea(
-            NSTrackingArea(
-                rect: .zero,
-                options: [.mouseEnteredAndExited, .activeAlways, .inVisibleRect],
-                owner: self,
-                userInfo: nil
-            )
-        )
-    }
-
-    override func hitTest(_ point: NSPoint) -> NSView? {
-        nil
-    }
-
-    override func mouseEntered(with event: NSEvent) {
-        onHover?(true, rootLocalFrame())
-    }
-
-    override func mouseExited(with event: NSEvent) {
-        onHover?(false, rootLocalFrame())
-    }
-
-    private func rootLocalFrame() -> CGRect {
-        guard let contentView = window?.contentView else { return .zero }
-        let rectInContent = contentView.convert(bounds, from: self)
-        let y = contentView.isFlipped
-            ? rectInContent.minY
-            : contentView.bounds.height - rectInContent.maxY
-        return CGRect(
-            x: rectInContent.minX,
-            y: y,
-            width: rectInContent.width,
-            height: rectInContent.height
-        )
-    }
 }
 
 enum BubblePlacement {
@@ -299,162 +125,5 @@
         }
         path.closeSubpath()
         return path
-    }
-}
-
-private struct DraggableAppIconView: NSViewRepresentable {
-    let icon: NSImage
-    let iconSize: CGFloat
-    let payload: String
-    let resetToken: Int
-    let onLongPress: () -> Void
-    let onDragEnd: () -> Void
-    let onClick: () -> Void
-
-    func makeNSView(context: Context) -> DragIconNSView {
-        let view = DragIconNSView()
-        view.image = icon
-        view.iconSize = iconSize
-        view.payload = payload
-        view.resetToken = resetToken
-        view.onLongPress = onLongPress
-        view.onDragEnd = onDragEnd
-        view.onClick = onClick
-        return view
-    }
-
-    func updateNSView(_ view: DragIconNSView, context: Context) {
-        let imageChanged = view.image !== icon
-        let sizeChanged = view.iconSize != iconSize
-        view.image = icon
-        view.iconSize = iconSize
-        view.payload = payload
-        if view.resetToken != resetToken {
-            view.resetToken = resetToken
-            view.resetInteractionState()
-        }
-        view.onLongPress = onLongPress
-        view.onDragEnd = onDragEnd
-        view.onClick = onClick
-        if imageChanged || sizeChanged {
-            view.needsDisplay = true
-        }
-    }
-}
-
-private final class DragIconNSView: NSView {
-    var image: NSImage = NSImage()
-    var iconSize: CGFloat = 56
-    var payload: String = ""
-    var resetToken = 0
-    var onLongPress: (() -> Void)?
-    var onDragEnd: (() -> Void)?
-    var onClick: (() -> Void)?
-
-    private var mouseDownEvent: NSEvent?
-    private var didStartDrag = false
-    private var isLongPressActive = false
-    private var longPressWorkItem: DispatchWorkItem?
-    override var isFlipped: Bool { true }
-
-    func resetInteractionState() {
-        longPressWorkItem?.cancel()
-        longPressWorkItem = nil
-        mouseDownEvent = nil
-        didStartDrag = false
-        isLongPressActive = false
-    }
-
-    override func draw(_ dirtyRect: NSRect) {
-        super.draw(dirtyRect)
-        let rect = NSRect(
-            x: (bounds.width - iconSize) / 2,
-            y: (bounds.height - iconSize) / 2,
-            width: iconSize,
-            height: iconSize
-        )
-        image.draw(in: rect)
-    }
-
-    override func mouseDown(with event: NSEvent) {
-        mouseDownEvent = event
-        didStartDrag = false
-        isLongPressActive = false
-        let workItem = DispatchWorkItem { [weak self] in
-            guard let self, self.mouseDownEvent != nil else { return }
-            self.isLongPressActive = true
-            self.onLongPress?()
-        }
-        longPressWorkItem = workItem
-        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: workItem)
-    }
-
-    override func mouseDragged(with event: NSEvent) {
-        if didStartDrag {
-            AppDragCoordinator.shared.updateDragLocation(
-                screenPoint(for: event),
-                copy: event.modifierFlags.contains(.option)
-            )
-            return
-        }
-
-        guard let mouseDownEvent, isLongPressActive else { return }
-        let dx = event.locationInWindow.x - mouseDownEvent.locationInWindow.x
-        let dy = event.locationInWindow.y - mouseDownEvent.locationInWindow.y
-        guard hypot(dx, dy) > 3 else { return }
-
-        didStartDrag = true
-        longPressWorkItem?.cancel()
-        AppDragCoordinator.shared.beginDrag(
-            image: makeDragImage(),
-            payload: payload,
-            at: screenPoint(for: event),
-            copy: event.modifierFlags.contains(.option),
-            in: window
-        )
-    }
-
-    override func mouseUp(with event: NSEvent) {
-        longPressWorkItem?.cancel()
-        if didStartDrag {
-            AppDragCoordinator.shared.finishDrag(
-                at: screenPoint(for: event),
-                copy: event.modifierFlags.contains(.option)
-            )
-            onDragEnd?()
-        } else if !isLongPressActive {
-            onClick?()
-        } else {
-            onDragEnd?()
-        }
-        didStartDrag = false
-        isLongPressActive = false
-        mouseDownEvent = nil
-        longPressWorkItem = nil
-    }
-
-    private func makeDragImage() -> NSImage {
-        let scale: CGFloat = 1.22 * 1.5
-        let imageSize = iconSize * scale
-        let padding = iconSize * 0.45
-        let canvasSize = NSSize(width: imageSize + padding * 2, height: imageSize + padding * 2)
-        let dragImage = NSImage(size: canvasSize)
-
-        dragImage.lockFocus()
-        NSGraphicsContext.current?.imageInterpolation = .high
-
-        let shadow = NSShadow()
-        shadow.shadowColor = NSColor.black.withAlphaComponent(0.48)
-        shadow.shadowBlurRadius = 24
-        shadow.shadowOffset = NSSize(width: 0, height: -14)
-        shadow.set()
-
-        image.draw(in: NSRect(x: padding, y: padding, width: imageSize, height: imageSize))
-        dragImage.unlockFocus()
-        return dragImage
-    }
-
-    private func screenPoint(for event: NSEvent) -> NSPoint {
-        window?.convertPoint(toScreen: event.locationInWindow) ?? NSEvent.mouseLocation
     }
 }

--
Gitblit v1.9.3