Skip to content

Commit

Permalink
tray items now appear immediately after being removed from bar
Browse files Browse the repository at this point in the history
  • Loading branch information
EricBAndrews committed Jan 29, 2025
1 parent 7223ce2 commit e5ba497
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,28 @@ extension InteractionBarEditorView {
}
}
}

@Observable
class TrayItem: Equatable {
let item: Configuration.Item

private(set) var opacity: CGFloat

init(item: Configuration.Item, visible: Bool) {
self.item = item
self.opacity = visible ? 1 : 0
}

/// Toggles opacity to 1
func show() { opacity = 1 }

/// Toggles opacity to 0
func hide() { opacity = 0 }

static func == (lhs: TrayItem, rhs: TrayItem) -> Bool {
lhs.item == rhs.item
}
}

@Observable
class BarItem: Equatable {
Expand Down Expand Up @@ -70,7 +92,7 @@ extension InteractionBarEditorView {
var allowNewItemInsertion: Bool {
if let trayPickedUpItem {
let currentScore = barItems.reduce(0) { $0 + ($1.item?.score ?? 0) }
return currentScore + trayPickedUpItem.score <= 6
return currentScore + trayPickedUpItem.item.score <= 6
}
return true
}
Expand Down Expand Up @@ -98,12 +120,12 @@ extension InteractionBarEditorView {
}
}

func trayItemDragGesture(item: Configuration.Item) -> some Gesture {
func trayItemDragGesture(trayItem: TrayItem) -> some Gesture {
DragGesture(minimumDistance: 0, coordinateSpace: .named("editor"))
.onChanged { gesture in
if trayPickedUpItem == nil, !barItems.contains(where: { $0.item == item }) {
if trayPickedUpItem == nil, !barItems.contains(where: { $0.item == trayItem.item }) {
HapticManager.main.play(haptic: .firmInfo, priority: .low)
trayPickedUpItem = item
trayPickedUpItem = trayItem
}
dragLocation = gesture.location
dragTranslation = gesture.translation
Expand All @@ -128,38 +150,39 @@ extension InteractionBarEditorView {
} else if let barPickedUpItem {
switch dropLocation {
case .bar(let targetIndex):
moveOnBar(item: barPickedUpItem.item, from: barPickedUpItem.index, to: targetIndex)
moveOnBar(barItem: barPickedUpItem.barItem, from: barPickedUpItem.index, to: targetIndex)
case .tray:
removeFromBar(item: barPickedUpItem.item)
removeFromBar(barItem: barPickedUpItem.barItem)
}
}
}

// MARK: - State Updates

func addToBar(_ item: Configuration.Item, at index: Int) {
func addToBar(_ trayItem: TrayItem, at index: Int) {
guard allowNewItemInsertion,
!barItems.contains(where: { $0.item == item }) else {
!barItems.contains(where: { $0.item == trayItem.item }) else {
assertionFailure(!allowNewItemInsertion ? "Item insertion disabled" : "Item already in bar")
return
}

HapticManager.main.play(haptic: .firmInfo, priority: .high)

let newItem: BarItem = .init(item: item, expanded: false, visible: true)
let newItem: BarItem = .init(item: trayItem.item, expanded: false, visible: true)
barItems.insert(newItem, at: index)
trayItem.hide()

updateConfiguration()
}

func moveOnBar(item: BarItem, from sourceIndex: Int, to targetIndex: Int) {
func moveOnBar(barItem: BarItem, from sourceIndex: Int, to targetIndex: Int) {
// noop on move to current location or immediately after current location
guard targetIndex != sourceIndex, targetIndex != sourceIndex + 1 else { return }

HapticManager.main.play(haptic: .firmInfo, priority: .high)

let newItem: BarItem = .init(item: item.item, expanded: false, visible: true, ancestor: item)
item.hide()
let newItem: BarItem = .init(item: barItem.item, expanded: false, visible: true, ancestor: barItem)
barItem.hide()

if targetIndex == barItems.count {
barItems.append(newItem)
Expand All @@ -169,26 +192,31 @@ extension InteractionBarEditorView {

// wait for animation to complete, then remove original item from barItems
DispatchQueue.main.asyncAfter(deadline: .now() + barAnimationDuration) {
barItems.removeAll(where: { $0 == item })
barItems.removeAll(where: { $0 == barItem })
updateConfiguration()
}
}

func removeFromBar(item: BarItem) {
func removeFromBar(barItem: BarItem) {
// no removing the info stack
guard item.item != nil else { return }
guard let item = barItem.item else { return }
guard let trayItem = trayItems.first(where: { $0.item == item }) else {
assertionFailure("Could not find \(item) in tray!")
return
}

HapticManager.main.play(haptic: .firmInfo, priority: .high)

// smoothly animate away
item.hide()
withAnimation(.easeInOut(duration: barAnimationDuration)) {
item.collapse()
barItem.hide()
withAnimation(.easeInOut(duration: barAnimationDuration)) {
trayItem.show()
barItem.collapse()
}

// wait for animation to complete, then remove from barItems
DispatchQueue.main.asyncAfter(deadline: .now() + barAnimationDuration) {
barItems.removeAll(where: { $0 == item })
barItems.removeAll(where: { $0 == barItem })
updateConfiguration()
}
}
Expand All @@ -207,9 +235,9 @@ extension InteractionBarEditorView {

// MARK: - Helpers

func trayItemOutlineColor(_ item: Configuration.Item) -> Color {
func trayItemOutlineColor(_ trayItem: TrayItem) -> Color {
if let dropLocation,
trayPickedUpItem == item || (barPickedUpItem?.item.item == item && dropLocation == .tray) {
trayPickedUpItem == trayItem || (barPickedUpItem?.barItem.item == trayItem.item && dropLocation == .tray) {
return palette.accent
}
return palette.tertiary
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,24 +122,24 @@ extension InteractionBarEditorView {
@ViewBuilder
var tray: some View {
HFlow(horizontalAlignment: .center, verticalAlignment: .center, distributeItemsEvenly: true) {
ForEach(trayItems, id: \.self) { trayItem($0) }
ForEach(trayItems, id: \.item) { trayItem($0) }
}
}

@ViewBuilder
func trayItem(_ item: Configuration.Item) -> some View {
itemLabel(item)
.opacity(barItems.contains(where: { $0.item == item }) ? 0 : 1)
func trayItem(_ trayItem: TrayItem) -> some View {
itemLabel(trayItem.item)
.opacity(trayItem.opacity)
.geometryGroup()
.offset(trayPickedUpItem == item ? dragTranslation : .zero)
.offset(trayPickedUpItem == trayItem ? dragTranslation : .zero)
.background {
Capsule()
.fill(trayItemOutlineColor(item).opacity(0.2))
.stroke(trayItemOutlineColor(item))
.fill(trayItemOutlineColor(trayItem).opacity(0.2))
.stroke(trayItemOutlineColor(trayItem))
.background(palette.background, in: .capsule)
}
.gesture(trayItemDragGesture(item: item))
.zIndex(trayPickedUpItem == item ? 2 : 0)
.gesture(trayItemDragGesture(trayItem: trayItem))
.zIndex(trayPickedUpItem == trayItem ? 2 : 0)
}

@ViewBuilder
Expand Down Expand Up @@ -210,7 +210,7 @@ extension InteractionBarEditorView {
.frame(height: infoCapsuleHeight)
} else if let trayPickedUpItem {
Group {
switch trayPickedUpItem {
switch trayPickedUpItem.item {
case let .action(action):
HStack {
Image(systemName: action.appearance.barIcon)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ struct InteractionBarEditorView<Configuration: InteractionBarConfiguration>: Vie
}
}

@State var trayItems: [Configuration.Item] = Configuration.Item.allCases
@State var trayItems: [TrayItem]
@State var barItems: [BarItem] = .init()

@State var barPickedUpItem: (item: BarItem, index: Int)?
@State var trayPickedUpItem: Configuration.Item?
@State var barPickedUpItem: (barItem: BarItem, index: Int)?
@State var trayPickedUpItem: TrayItem?

/// Current entity the dragged item is hovered over. -1 indicates the tray.
@State var dropLocation: DropLocation?
Expand All @@ -40,9 +40,13 @@ struct InteractionBarEditorView<Configuration: InteractionBarConfiguration>: Vie
init(configuration: Configuration, onSet: @escaping (Configuration) -> Void) {
self.configuration = configuration
self.onSet = onSet
self._barItems = .init(wrappedValue: (configuration.leading + [nil] + configuration.trailing).map { item in
let configurationItems: [Configuration.Item?] = configuration.leading + [nil] + configuration.trailing
self._barItems = .init(wrappedValue: configurationItems.map { item in
.init(item: item, expanded: true, visible: true)
})
self._trayItems = .init(wrappedValue: Configuration.Item.allCases.map { item in
TrayItem(item: item, visible: !configurationItems.contains(item))
})
}

init(setting: WritableKeyPath<InteractionBarTracker, Configuration>) {
Expand Down

0 comments on commit e5ba497

Please sign in to comment.