From 68ee097bca72df98ba1a3c57e8ededa1c2665f79 Mon Sep 17 00:00:00 2001 From: XN Date: Tue, 14 Jul 2026 03:30:11 +0800 Subject: [PATCH] cleanup: remove dead code and reorganize file structure Dead code removed: - DockPanel: iconSize, containerView, addDragHandle() (drag handle was added via DockDragHandleRepresentable in SwiftUI instead) - AppDelegate: stale panel.iconSize assignments, commented-out menu item - DockDragHandleView: commented-out calculation - DockState: unused items property (items read from @Binding instead) Reorganized: - Models/: AppInfo (from Views/), DockState (from root/) - Views/: DockDragHandleRepresentable (from Windows/), ScreenRectReader (extracted from TooltipManager.swift) - Windows/: DockResizeHandleView (from Views/) - Utilities/: new folder, moved Extension.swift from root/ --- Sources/FreeDock/AppDelegate.swift | 44 +++++++++++----- .../FreeDock/Managers/TooltipManager.swift | 15 ------ .../FreeDock/{Views => Models}/AppInfo.swift | 0 Sources/FreeDock/Models/DockState.swift | 10 ++++ Sources/FreeDock/Utilities/Extension.swift | 5 ++ Sources/FreeDock/Views/DockContentView.swift | 20 ++++++-- .../DockDragHandleRepresentable.swift | 0 Sources/FreeDock/Views/DockItemView.swift | 4 +- .../Views/DockResizeHandleRepresentable.swift | 20 ++++++++ Sources/FreeDock/Views/ScreenRectReader.swift | 17 +++++++ .../FreeDock/Windows/DockDragHandleView.swift | 5 +- Sources/FreeDock/Windows/DockPanel.swift | 18 ------- .../FreeDock/Windows/DockPanelDelegate.swift | 3 ++ .../Windows/DockResizeHandleView.swift | 51 +++++++++++++++++++ 14 files changed, 159 insertions(+), 53 deletions(-) rename Sources/FreeDock/{Views => Models}/AppInfo.swift (100%) create mode 100644 Sources/FreeDock/Models/DockState.swift create mode 100644 Sources/FreeDock/Utilities/Extension.swift rename Sources/FreeDock/{Windows => Views}/DockDragHandleRepresentable.swift (100%) create mode 100644 Sources/FreeDock/Views/DockResizeHandleRepresentable.swift create mode 100644 Sources/FreeDock/Views/ScreenRectReader.swift create mode 100644 Sources/FreeDock/Windows/DockResizeHandleView.swift diff --git a/Sources/FreeDock/AppDelegate.swift b/Sources/FreeDock/AppDelegate.swift index b29913c..b91bf75 100644 --- a/Sources/FreeDock/AppDelegate.swift +++ b/Sources/FreeDock/AppDelegate.swift @@ -10,7 +10,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { .appendingPathComponent(".config/freedock.json") ) private var _lockPositions = false - + private var dockStates: [UUID: DockState] = [:] private struct IconSizeSelection { let dockID: UUID let size: Double @@ -97,8 +97,6 @@ class AppDelegate: NSObject, NSApplicationDelegate { let iconItem = NSMenuItem(title: "Icon Size", action: nil, keyEquivalent: "") iconItem.submenu = iconMenu - dockMenu.addItem(iconItem) - let root = NSMenuItem(title: dock.name, action: nil, keyEquivalent: "") root.submenu = dockMenu @@ -251,19 +249,13 @@ class AppDelegate: NSObject, NSApplicationDelegate { @objc private func changeIconSize(_ sender: NSMenuItem) { guard let selection = sender.representedObject as? IconSizeSelection, let index = configManager.config.docks.firstIndex(where: { $0.id == selection.dockID }) - else { - return - } + else { return } configManager.config.docks[index].iconSize = selection.size configManager.save() - if let panel = dockPanels[selection.dockID] { - panel.close() - dockPanels.removeValue(forKey: selection.dockID) - } - - showDock(configManager.config.docks[index]) + dockStates[selection.dockID]?.iconSize = selection.size + dockPanels[selection.dockID]?.resizeToFitContent() rebuildMenu() } @@ -274,6 +266,9 @@ class AppDelegate: NSObject, NSApplicationDelegate { panel.clampToVisibleFrame() let dockID = config.id + let state = DockState(iconSize: config.iconSize) + dockStates[config.id] = state // ← store it + let content = DockContentView( panel: panel, items: Binding( @@ -284,7 +279,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { } ), orientation: config.orientation, - iconSize: config.iconSize, + state: state, onItemsChanged: { _ in self.configManager.save() DispatchQueue.main.async { panel.resizeToFitContent() } @@ -332,4 +327,27 @@ extension AppDelegate: DockPanelDelegate { configManager.config.docks[idx].position = snapped.origin configManager.save() } + + func currentIconSize(for panel: DockPanel) -> Double { + dockStates[panel.dockID]?.iconSize ?? 48 + } + + func dockPanelDidResize(_ panel: DockPanel, proposedIconSize: Double) { + guard let idx = configManager.config.docks.firstIndex(where: { $0.id == panel.dockID }) else { return } + + // Update reactive state → SwiftUI reflows content + dockStates[panel.dockID]?.iconSize = proposedIconSize + configManager.config.docks[idx].iconSize = proposedIconSize + + // Fit panel to the new content size + DispatchQueue.main.async { + panel.resizeToFitContent() + } + } + + func dockPanelDidFinishResize(_ panel: DockPanel) { + guard let idx = configManager.config.docks.firstIndex(where: { $0.id == panel.dockID }) else { return } + configManager.config.docks[idx].position = panel.frame.origin + configManager.save() + } } diff --git a/Sources/FreeDock/Managers/TooltipManager.swift b/Sources/FreeDock/Managers/TooltipManager.swift index 8910ece..a08c659 100644 --- a/Sources/FreeDock/Managers/TooltipManager.swift +++ b/Sources/FreeDock/Managers/TooltipManager.swift @@ -56,18 +56,3 @@ final class TooltipManager { panel = p } } - -/// Helper to read a SwiftUI view's screen rect via AppKit -struct ScreenRectReader: NSViewRepresentable { - let onRect: (NSRect) -> Void - func makeNSView(context _: Context) -> NSView { - NSView() - } - - func updateNSView(_ nsView: NSView, context _: Context) { - DispatchQueue.main.async { - guard let win = nsView.window else { return } - onRect(win.convertToScreen(nsView.convert(nsView.bounds, to: nil))) - } - } -} diff --git a/Sources/FreeDock/Views/AppInfo.swift b/Sources/FreeDock/Models/AppInfo.swift similarity index 100% rename from Sources/FreeDock/Views/AppInfo.swift rename to Sources/FreeDock/Models/AppInfo.swift diff --git a/Sources/FreeDock/Models/DockState.swift b/Sources/FreeDock/Models/DockState.swift new file mode 100644 index 0000000..aa75758 --- /dev/null +++ b/Sources/FreeDock/Models/DockState.swift @@ -0,0 +1,10 @@ +import SwiftUI + +@MainActor +class DockState: ObservableObject { + @Published var iconSize: Double + + init(iconSize: Double) { + self.iconSize = iconSize + } +} diff --git a/Sources/FreeDock/Utilities/Extension.swift b/Sources/FreeDock/Utilities/Extension.swift new file mode 100644 index 0000000..1e0ae9e --- /dev/null +++ b/Sources/FreeDock/Utilities/Extension.swift @@ -0,0 +1,5 @@ +extension Comparable { + func clamped(to range: ClosedRange) -> Self { + min(max(self, range.lowerBound), range.upperBound) + } +} diff --git a/Sources/FreeDock/Views/DockContentView.swift b/Sources/FreeDock/Views/DockContentView.swift index ca71f0a..ec095fc 100644 --- a/Sources/FreeDock/Views/DockContentView.swift +++ b/Sources/FreeDock/Views/DockContentView.swift @@ -5,10 +5,14 @@ struct DockContentView: View { let panel: DockPanel @Binding var items: [DockItem] let orientation: Orientation - let iconSize: Double + @ObservedObject var state: DockState let onItemsChanged: @MainActor ([DockItem]) -> Void let onAppLaunch: @MainActor (DockItem) -> Void + private var iconSize: Double { + state.iconSize + } + @State private var isTargeted = false @State private var dropPulse = false @State private var draggedItem: DockItem? @@ -31,6 +35,11 @@ struct DockContentView: View { .frame(width: 24) HStack(spacing: 0) { content } .padding(4) + DockResizeHandleRepresentable( + panel: panel, + orientation: orientation + ) + .frame(width: 12) } .contentShape(Rectangle()) .onHover { hovering in @@ -66,6 +75,11 @@ struct DockContentView: View { .padding(.top, 12) VStack(spacing: 0) { content } .padding(4) + DockResizeHandleRepresentable( + panel: panel, + orientation: orientation + ) + .frame(height: 12) } .contentShape(Rectangle()) .onHover { hovering in @@ -214,8 +228,8 @@ struct DockContentView: View { } switch abs(index - hoveredIndex) { - case 0: return 1.30 - case 1: return 1.15 + case 0: return 1.20 + case 1: return 1.10 case 2: return 1.05 default: return 1.0 } diff --git a/Sources/FreeDock/Windows/DockDragHandleRepresentable.swift b/Sources/FreeDock/Views/DockDragHandleRepresentable.swift similarity index 100% rename from Sources/FreeDock/Windows/DockDragHandleRepresentable.swift rename to Sources/FreeDock/Views/DockDragHandleRepresentable.swift diff --git a/Sources/FreeDock/Views/DockItemView.swift b/Sources/FreeDock/Views/DockItemView.swift index e6563d5..a561b56 100644 --- a/Sources/FreeDock/Views/DockItemView.swift +++ b/Sources/FreeDock/Views/DockItemView.swift @@ -60,8 +60,8 @@ struct DockItemView: View { .animation(.easeOut(duration: 0.15), value: isHovering) .padding(4) .scaleEffect(scale) - .offset(y: bouncing ? -8 : 0) - .animation(.interpolatingSpring(stiffness: 300, damping: 8), value: bouncing) + .offset(y: bouncing ? -4 : 0) + .animation(.interpolatingSpring(stiffness: 300, damping: 4), value: bouncing) .animation(.spring(response: 0.25, dampingFraction: 0.7), value: scale) .offset(y: isHovering ? -2 : 0) .shadow(radius: isHovering ? 5 : 2) diff --git a/Sources/FreeDock/Views/DockResizeHandleRepresentable.swift b/Sources/FreeDock/Views/DockResizeHandleRepresentable.swift new file mode 100644 index 0000000..fc79b1e --- /dev/null +++ b/Sources/FreeDock/Views/DockResizeHandleRepresentable.swift @@ -0,0 +1,20 @@ +import AppKit +import SwiftUI + +@MainActor +struct DockResizeHandleRepresentable: NSViewRepresentable { + let panel: DockPanel + let orientation: Orientation + + func makeNSView(context _: Context) -> DockResizeHandleView { + let view = DockResizeHandleView(frame: .zero) + view.dockPanel = panel + view.orientation = orientation + return view + } + + func updateNSView(_ view: DockResizeHandleView, context _: Context) { + view.dockPanel = panel + view.orientation = orientation + } +} diff --git a/Sources/FreeDock/Views/ScreenRectReader.swift b/Sources/FreeDock/Views/ScreenRectReader.swift new file mode 100644 index 0000000..16589c5 --- /dev/null +++ b/Sources/FreeDock/Views/ScreenRectReader.swift @@ -0,0 +1,17 @@ +import AppKit +import SwiftUI + +/// Helper to read a SwiftUI view's screen rect via AppKit +struct ScreenRectReader: NSViewRepresentable { + let onRect: (NSRect) -> Void + func makeNSView(context _: Context) -> NSView { + NSView() + } + + func updateNSView(_ nsView: NSView, context _: Context) { + DispatchQueue.main.async { + guard let win = nsView.window else { return } + onRect(win.convertToScreen(nsView.convert(nsView.bounds, to: nil))) + } + } +} diff --git a/Sources/FreeDock/Windows/DockDragHandleView.swift b/Sources/FreeDock/Windows/DockDragHandleView.swift index a9bb835..30240f6 100644 --- a/Sources/FreeDock/Windows/DockDragHandleView.swift +++ b/Sources/FreeDock/Windows/DockDragHandleView.swift @@ -61,8 +61,9 @@ class DockDragHandleView: NSView { override func layout() { super.layout() - let pillW: CGFloat = 3 - let pillH: CGFloat = 22 + let pillW: CGFloat = 5 + let pillH = CGFloat(22) + if orientation == .horizontal { highlightLayer.frame = NSRect( x: (stripRect.width - pillW) / 2 + stripRect.minX, diff --git a/Sources/FreeDock/Windows/DockPanel.swift b/Sources/FreeDock/Windows/DockPanel.swift index c732fb0..587562e 100644 --- a/Sources/FreeDock/Windows/DockPanel.swift +++ b/Sources/FreeDock/Windows/DockPanel.swift @@ -7,7 +7,6 @@ class DockPanel: NSPanel { var dockOrientation: Orientation = .horizontal weak var dockDelegate: DockPanelDelegate? private weak var hostingView: NSView? - private var containerView: NSView? private var hideWorkItem: DispatchWorkItem? private func enforcedSize(for intrinsicSize: NSSize) -> NSSize { @@ -49,7 +48,6 @@ class DockPanel: NSPanel { func setContentView(_ view: V) { let container = DockContainerView(frame: NSRect(origin: .zero, size: NSSize(width: 400, height: 70))) container.dockPanel = self - containerView = container let hosting = NSHostingView(rootView: view) hosting.frame = container.bounds @@ -57,7 +55,6 @@ class DockPanel: NSPanel { hosting.wantsLayer = true hosting.layer?.cornerRadius = 14 container.addSubview(hosting) - container.addSubview(hosting) hostingView = hosting contentView = container let intrinsicSize = hosting.intrinsicContentSize @@ -80,21 +77,6 @@ class DockPanel: NSPanel { setContentSize(enforcedSize) } - func addDragHandle(orientation: Orientation) { - guard let container = containerView else { - os_log(.error, "containerView is nil") - return - } - let handle = DockDragHandleView(frame: container.bounds) - - handle.autoresizingMask = [.width, .height] - handle.orientation = orientation - handle.wantsLayer = true - handle.layer?.backgroundColor = NSColor.clear.cgColor - handle.dockPanel = self - container.addSubview(handle, positioned: .above, relativeTo: nil) - } - /// Prevent docks from landing off-screen (e.g., after monitor disconnect) func clampToVisibleFrame() { guard let screen = NSScreen.main else { return } diff --git a/Sources/FreeDock/Windows/DockPanelDelegate.swift b/Sources/FreeDock/Windows/DockPanelDelegate.swift index 8f2c8ef..9cae1c1 100644 --- a/Sources/FreeDock/Windows/DockPanelDelegate.swift +++ b/Sources/FreeDock/Windows/DockPanelDelegate.swift @@ -4,4 +4,7 @@ import Foundation protocol DockPanelDelegate: AnyObject { var lockPositions: Bool { get } func dockPanelDidMove(_ panel: DockPanel) + func dockPanelDidResize(_ panel: DockPanel, proposedIconSize: Double) + func dockPanelDidFinishResize(_ panel: DockPanel) + func currentIconSize(for panel: DockPanel) -> Double } diff --git a/Sources/FreeDock/Windows/DockResizeHandleView.swift b/Sources/FreeDock/Windows/DockResizeHandleView.swift new file mode 100644 index 0000000..9f5c6ba --- /dev/null +++ b/Sources/FreeDock/Windows/DockResizeHandleView.swift @@ -0,0 +1,51 @@ +import Cocoa + +class DockResizeHandleView: NSView { + weak var dockPanel: DockPanel? + var orientation: Orientation = .horizontal + + private var dragStartLocation: NSPoint = .zero + private var dragStartIconSize: Double = 48 + + override init(frame: NSRect) { + super.init(frame: frame) + wantsLayer = true + layer?.backgroundColor = NSColor.clear.cgColor + } + + required init?(coder _: NSCoder) { + nil + } + + override func resetCursorRects() { + discardCursorRects() + addCursorRect(bounds, cursor: orientation == .horizontal ? .resizeLeftRight : .resizeUpDown) + } + + override func acceptsFirstMouse(for _: NSEvent?) -> Bool { + true + } + + override func mouseDown(with _: NSEvent) { + guard let panel = dockPanel else { return } + dragStartLocation = NSEvent.mouseLocation + // Snapshot the current icon size at drag start + dragStartIconSize = panel.dockDelegate?.currentIconSize(for: panel) ?? 48 + } + + override func mouseDragged(with _: NSEvent) { + guard let panel = dockPanel else { return } + let current = NSEvent.mouseLocation + let delta = orientation == .horizontal + ? current.x - dragStartLocation.x + : -(current.y - dragStartLocation.y) // drag down = smaller for vertical + + let newSize = (dragStartIconSize + delta * 0.4).clamped(to: 16 ... 128) + panel.dockDelegate?.dockPanelDidResize(panel, proposedIconSize: newSize) + } + + override func mouseUp(with _: NSEvent) { + guard let panel = dockPanel else { return } + panel.dockDelegate?.dockPanelDidFinishResize(panel) + } +}