Skip to content

Commit 9f37f87

Browse files
committed
ToolbarCustomize komplett neu — Settings-style NSPanel, proper WindowDelegate, DnD current/available zones, same buttons wie Settings. alte Sections+View geloescht
1 parent f3b7e65 commit 9f37f87

11 files changed

Lines changed: 420 additions & 501 deletions

GUI/Resources/curr_version.asc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2026.04.22 04:37:22 at Host: NEVA
1+
2026.04.22 20:54:22 at Host: NEVA
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// ToolbarCustChip.swift
2+
// MiMiNavigator
3+
//
4+
// Created by Claude on 24.04.2026.
5+
// Copyright © 2026 Senatov. All rights reserved.
6+
// Description: Draggable chip representing a toolbar button in the "Current Toolbar" strip.
7+
8+
import SwiftUI
9+
10+
// MARK: - Toolbar Customize Chip
11+
struct ToolbarCustChip: View {
12+
let item: ToolbarItemID
13+
let isInToolbar: Bool
14+
let isDragging: Bool
15+
16+
var body: some View {
17+
VStack(spacing: 3) {
18+
Image(systemName: item.systemImage)
19+
.font(.system(size: 16, weight: .medium))
20+
.symbolRenderingMode(.hierarchical)
21+
.foregroundStyle(Color.accentColor)
22+
.frame(width: 38, height: 30)
23+
.background(
24+
RoundedRectangle(cornerRadius: 7, style: .continuous)
25+
.fill(Color.accentColor.opacity(0.10))
26+
)
27+
Text(item.label)
28+
.font(.system(size: 9, weight: .medium))
29+
.foregroundStyle(.primary)
30+
.lineLimit(1)
31+
}
32+
.opacity(isDragging ? 0.35 : 1.0)
33+
.help(item.helpText)
34+
}
35+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// ToolbarCustDropDelegates.swift
2+
// MiMiNavigator
3+
//
4+
// Created by Claude on 24.04.2026.
5+
// Copyright © 2026 Senatov. All rights reserved.
6+
// Description: D-n-D delegates for toolbar customization.
7+
// Reorder within Current Toolbar strip, append from palette.
8+
9+
import SwiftUI
10+
11+
// MARK: - Reorder within Current Toolbar
12+
struct ToolbarCustReorderDelegate: DropDelegate {
13+
let target: ToolbarItemID
14+
let store: ToolbarStore
15+
@Binding var dragItem: ToolbarItemID?
16+
17+
func dropUpdated(info: DropInfo) -> DropProposal? {
18+
DropProposal(operation: .move)
19+
}
20+
21+
func performDrop(info: DropInfo) -> Bool {
22+
dragItem = nil
23+
return true
24+
}
25+
26+
func dropEntered(info: DropInfo) {
27+
guard let from = dragItem, from != target else { return }
28+
// If dragged item is not visible yet — make it visible first
29+
if !store.visibleIDs.contains(from) {
30+
store.toggleVisibility(from)
31+
}
32+
guard
33+
let fromIdx = store.orderedIDs.firstIndex(of: from),
34+
let targetIdx = store.orderedIDs.firstIndex(of: target)
35+
else { return }
36+
let dest = targetIdx < fromIdx ? targetIdx : targetIdx + 1
37+
store.move(fromOffsets: IndexSet(integer: fromIdx), toOffset: dest)
38+
}
39+
}
40+
41+
42+
// MARK: - Append to end of Current Toolbar
43+
struct ToolbarCustAppendDelegate: DropDelegate {
44+
let store: ToolbarStore
45+
@Binding var dragItem: ToolbarItemID?
46+
47+
func dropUpdated(info: DropInfo) -> DropProposal? {
48+
DropProposal(operation: .copy)
49+
}
50+
51+
func performDrop(info: DropInfo) -> Bool {
52+
if let item = dragItem, !store.visibleIDs.contains(item) {
53+
store.toggleVisibility(item)
54+
}
55+
dragItem = nil
56+
return true
57+
}
58+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// ToolbarCustDropPlaceholder.swift
2+
// MiMiNavigator
3+
//
4+
// Created by Claude on 24.04.2026.
5+
// Copyright © 2026 Senatov. All rights reserved.
6+
// Description: Dashed drop placeholder at the end of Current Toolbar strip.
7+
8+
import SwiftUI
9+
10+
// MARK: - Drop Placeholder
11+
struct ToolbarCustDropPlaceholder: View {
12+
var body: some View {
13+
RoundedRectangle(cornerRadius: 7, style: .continuous)
14+
.strokeBorder(
15+
Color.accentColor.opacity(0.45),
16+
style: StrokeStyle(lineWidth: 1.5, dash: [5, 3])
17+
)
18+
.frame(width: 38, height: 30)
19+
.overlay(
20+
Image(systemName: "plus")
21+
.font(.system(size: 13, weight: .medium))
22+
.foregroundStyle(Color.accentColor.opacity(0.5))
23+
)
24+
}
25+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// ToolbarCustPaletteCell.swift
2+
// MiMiNavigator
3+
//
4+
// Created by Claude on 24.04.2026.
5+
// Copyright © 2026 Senatov. All rights reserved.
6+
// Description: Grid cell in "Available Items" palette. Click toggles visibility.
7+
8+
import SwiftUI
9+
10+
// MARK: - Palette Cell
11+
struct ToolbarCustPaletteCell: View {
12+
let item: ToolbarItemID
13+
let isVisible: Bool
14+
let onToggle: () -> Void
15+
16+
var body: some View {
17+
Button(action: onToggle) {
18+
VStack(spacing: 4) {
19+
ZStack(alignment: .topTrailing) {
20+
RoundedRectangle(cornerRadius: 8, style: .continuous)
21+
.fill(isVisible ? Color.accentColor.opacity(0.12) : Color.gray.opacity(0.06))
22+
.frame(width: 46, height: 38)
23+
.overlay(
24+
RoundedRectangle(cornerRadius: 8, style: .continuous)
25+
.strokeBorder(
26+
isVisible ? Color.accentColor.opacity(0.3) : Color.gray.opacity(0.15),
27+
lineWidth: 0.8
28+
)
29+
)
30+
Image(systemName: item.systemImage)
31+
.font(.system(size: 17, weight: .medium))
32+
.symbolRenderingMode(.hierarchical)
33+
.foregroundStyle(isVisible ? Color.accentColor : Color.secondary.opacity(0.45))
34+
.frame(width: 46, height: 38)
35+
Image(systemName: isVisible ? "checkmark.circle.fill" : "circle")
36+
.font(.system(size: 11))
37+
.foregroundStyle(isVisible ? Color.accentColor : Color.secondary.opacity(0.4))
38+
.offset(x: 5, y: -5)
39+
}
40+
Text(item.label)
41+
.font(.system(size: 10, weight: isVisible ? .medium : .regular))
42+
.foregroundStyle(isVisible ? Color.primary : Color.secondary.opacity(0.6))
43+
.lineLimit(2)
44+
.multilineTextAlignment(.center)
45+
.frame(width: 72)
46+
}
47+
}
48+
.buttonStyle(.plain)
49+
.opacity(isVisible ? 1.0 : 0.6)
50+
.help(item.helpText)
51+
.animation(.easeInOut(duration: 0.15), value: isVisible)
52+
}
53+
}
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
// ToolbarCustomizeRootView.swift
2+
// MiMiNavigator
3+
//
4+
// Created by Claude on 24.04.2026.
5+
// Copyright © 2026 Senatov. All rights reserved.
6+
// Description: Root view for toolbar customization dialog.
7+
// Classic macOS style: "Current Toolbar" strip on top (D-n-D reorder, drag out to remove),
8+
// "Available Items" palette below (drag into Current to add).
9+
// Same button style as Settings. Single-screen dialog.
10+
11+
import SwiftUI
12+
13+
// MARK: - Toolbar Customize Root View
14+
struct ToolbarCustomizeRootView: View {
15+
16+
let onDismiss: () -> Void
17+
@State private var store = ToolbarStore.shared
18+
@State private var showResetConfirm = false
19+
@State private var dragItem: ToolbarItemID? = nil
20+
21+
var body: some View {
22+
VStack(spacing: 0) {
23+
currentToolbarSection
24+
Divider()
25+
menuBarToggleRow
26+
Divider()
27+
availableItemsSection
28+
Divider()
29+
footerButtons
30+
}
31+
.background(DialogColors.base)
32+
.confirmationDialog("Reset Toolbar", isPresented: $showResetConfirm) {
33+
Button("Reset to Defaults", role: .destructive) { store.resetToDefaults() }
34+
Button("Cancel", role: .cancel) {}
35+
} message: {
36+
Text("Toolbar order and visibility will be reset to factory defaults.")
37+
}
38+
}
39+
40+
// MARK: - Current Toolbar (D-n-D reorder zone)
41+
private var currentToolbarSection: some View {
42+
VStack(alignment: .leading, spacing: 6) {
43+
Text("CURRENT TOOLBAR")
44+
.font(.system(size: 10, weight: .medium))
45+
.foregroundStyle(.secondary)
46+
.padding(.horizontal, 14)
47+
.padding(.top, 10)
48+
49+
currentToolbarStrip
50+
.padding(.horizontal, 12)
51+
.padding(.bottom, 8)
52+
53+
Text("Drag to reorder · drag out to remove")
54+
.font(.system(size: 10))
55+
.foregroundStyle(.tertiary)
56+
.padding(.horizontal, 14)
57+
.padding(.bottom, 6)
58+
}
59+
.background(DialogColors.light)
60+
}
61+
62+
private var currentToolbarStrip: some View {
63+
HStack(spacing: 8) {
64+
ForEach(store.visibleItems) { item in
65+
ToolbarCustChip(item: item, isInToolbar: true, isDragging: dragItem == item)
66+
.onDrag {
67+
dragItem = item
68+
return NSItemProvider(object: item.rawValue as NSString)
69+
}
70+
.onDrop(of: [.text], delegate: ToolbarCustReorderDelegate(
71+
target: item, store: store, dragItem: $dragItem
72+
))
73+
}
74+
// Drop zone for appending at end
75+
if store.visibleItems.count < store.orderedIDs.count {
76+
ToolbarCustDropPlaceholder()
77+
.onDrop(of: [.text], delegate: ToolbarCustAppendDelegate(
78+
store: store, dragItem: $dragItem
79+
))
80+
}
81+
Spacer(minLength: 0)
82+
}
83+
.frame(minHeight: 48)
84+
.padding(8)
85+
.background(
86+
RoundedRectangle(cornerRadius: 10, style: .continuous)
87+
.fill(Color(nsColor: .controlBackgroundColor).opacity(0.6))
88+
)
89+
.overlay(
90+
RoundedRectangle(cornerRadius: 10, style: .continuous)
91+
.strokeBorder(Color(nsColor: .separatorColor).opacity(0.4), lineWidth: 0.5)
92+
)
93+
}
94+
95+
// MARK: - Menu Bar Toggle
96+
private var menuBarToggleRow: some View {
97+
HStack(spacing: 10) {
98+
Image(systemName: "menubar.rectangle")
99+
.font(.system(size: 14))
100+
.foregroundStyle(store.menuBarVisible ? Color.accentColor : Color.secondary.opacity(0.5))
101+
.frame(width: 22)
102+
VStack(alignment: .leading, spacing: 1) {
103+
Text("Show Menu Bar")
104+
.font(.system(size: 13, weight: .medium))
105+
Text("Files · Mark · Commands · Net · Show · Configuration")
106+
.font(.system(size: 10))
107+
.foregroundStyle(.tertiary)
108+
.lineLimit(1)
109+
}
110+
Spacer()
111+
Toggle("", isOn: Binding(
112+
get: { store.menuBarVisible },
113+
set: { store.menuBarVisible = $0 }
114+
))
115+
.toggleStyle(.switch)
116+
.controlSize(.small)
117+
.labelsHidden()
118+
}
119+
.padding(.horizontal, 14)
120+
.padding(.vertical, 8)
121+
.background(store.menuBarVisible ? Color.accentColor.opacity(0.04) : Color.clear)
122+
}
123+
124+
// MARK: - Available Items (palette)
125+
private var availableItemsSection: some View {
126+
VStack(alignment: .leading, spacing: 6) {
127+
Text("AVAILABLE ITEMS")
128+
.font(.system(size: 10, weight: .medium))
129+
.foregroundStyle(.secondary)
130+
.padding(.horizontal, 14)
131+
.padding(.top, 8)
132+
ScrollView {
133+
LazyVGrid(
134+
columns: [GridItem(.adaptive(minimum: 90, maximum: 110), spacing: 10)],
135+
spacing: 10
136+
) {
137+
ForEach(store.orderedIDs.filter { !$0.isFixed }) { item in
138+
let isVisible = store.visibleIDs.contains(item)
139+
ToolbarCustPaletteCell(
140+
item: item,
141+
isVisible: isVisible,
142+
onToggle: { store.toggleVisibility(item) }
143+
)
144+
.onDrag {
145+
dragItem = item
146+
return NSItemProvider(object: item.rawValue as NSString)
147+
}
148+
}
149+
}
150+
.padding(.horizontal, 12)
151+
.padding(.bottom, 10)
152+
}
153+
.frame(maxHeight: .infinity)
154+
}
155+
.background(DialogColors.base)
156+
}
157+
158+
// MARK: - Footer
159+
private var footerButtons: some View {
160+
HStack(spacing: 12) {
161+
DownToolbarButtonView(title: "Reset", systemImage: "arrow.counterclockwise") {
162+
showResetConfirm = true
163+
}
164+
Spacer()
165+
Text("\(store.visibleItems.count) of \(store.orderedIDs.filter { !$0.isFixed }.count) shown")
166+
.font(.system(size: 11))
167+
.foregroundStyle(.tertiary)
168+
Spacer()
169+
DownToolbarButtonView(title: "Done", systemImage: "checkmark") {
170+
onDismiss()
171+
}
172+
}
173+
.padding(.horizontal, 14)
174+
.padding(.vertical, 10)
175+
.background(DialogColors.stripe)
176+
}
177+
}

0 commit comments

Comments
 (0)