|
@@ -57,7 +57,16 @@ final class PhotoPreviewOverlayView: NSView, AppearanceRefreshable {
|
|
|
private var cropOverlay: PhotoCropOverlayView?
|
|
private var cropOverlay: PhotoCropOverlayView?
|
|
|
private let printButton = PhotoPrintButton()
|
|
private let printButton = PhotoPrintButton()
|
|
|
|
|
|
|
|
- private var imageContainerTopConstraint: NSLayoutConstraint!
|
|
|
|
|
|
|
+ private var imageContainerTopToPreviewConstraint: NSLayoutConstraint?
|
|
|
|
|
+ private var imageContainerTopToControlsConstraint: NSLayoutConstraint?
|
|
|
|
|
+ private var imageContainerMinHeightConstraint: NSLayoutConstraint?
|
|
|
|
|
+ private var editControlsBottomConstraint: NSLayoutConstraint?
|
|
|
|
|
+ private var whiteningPanelHeightConstraint: NSLayoutConstraint?
|
|
|
|
|
+ private var saturationPanelHeightConstraint: NSLayoutConstraint?
|
|
|
|
|
+ private var contrastPanelHeightConstraint: NSLayoutConstraint?
|
|
|
|
|
+
|
|
|
|
|
+ private static let adjustmentPanelHeight: CGFloat = 56
|
|
|
|
|
+ private static let editingImageMinHeight: CGFloat = 320
|
|
|
|
|
|
|
|
private var isEditPanelVisible = false
|
|
private var isEditPanelVisible = false
|
|
|
private var isCropMode = false
|
|
private var isCropMode = false
|
|
@@ -177,7 +186,9 @@ final class PhotoPreviewOverlayView: NSView, AppearanceRefreshable {
|
|
|
printButton.translatesAutoresizingMaskIntoConstraints = false
|
|
printButton.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
|
|
|
editPanel.isHidden = true
|
|
editPanel.isHidden = true
|
|
|
- hideAllAdjustmentPanels()
|
|
|
|
|
|
|
+ whiteningPanel.isHidden = true
|
|
|
|
|
+ saturationPanel.isHidden = true
|
|
|
|
|
+ contrastPanel.isHidden = true
|
|
|
|
|
|
|
|
backButton.onClick = { [weak self] in self?.dismiss() }
|
|
backButton.onClick = { [weak self] in self?.dismiss() }
|
|
|
editButton.onClick = { [weak self] in self?.toggleEditPanel() }
|
|
editButton.onClick = { [weak self] in self?.toggleEditPanel() }
|
|
@@ -203,9 +214,9 @@ final class PhotoPreviewOverlayView: NSView, AppearanceRefreshable {
|
|
|
addSubview(titleLabel)
|
|
addSubview(titleLabel)
|
|
|
addSubview(counterLabel)
|
|
addSubview(counterLabel)
|
|
|
addSubview(previewSection)
|
|
addSubview(previewSection)
|
|
|
- previewSection.addSubview(editControlsArea)
|
|
|
|
|
previewSection.addSubview(imageContainer)
|
|
previewSection.addSubview(imageContainer)
|
|
|
- editControlsArea.addSubview(editButton)
|
|
|
|
|
|
|
+ previewSection.addSubview(editButton)
|
|
|
|
|
+ previewSection.addSubview(editControlsArea)
|
|
|
editControlsArea.addSubview(editPanel)
|
|
editControlsArea.addSubview(editPanel)
|
|
|
editControlsArea.addSubview(whiteningPanel)
|
|
editControlsArea.addSubview(whiteningPanel)
|
|
|
editControlsArea.addSubview(saturationPanel)
|
|
editControlsArea.addSubview(saturationPanel)
|
|
@@ -213,6 +224,22 @@ final class PhotoPreviewOverlayView: NSView, AppearanceRefreshable {
|
|
|
imageContainer.addSubview(imageView)
|
|
imageContainer.addSubview(imageView)
|
|
|
addSubview(printButton)
|
|
addSubview(printButton)
|
|
|
|
|
|
|
|
|
|
+ imageContainerTopToPreviewConstraint = imageContainer.topAnchor.constraint(
|
|
|
|
|
+ equalTo: editButton.bottomAnchor,
|
|
|
|
|
+ constant: 12
|
|
|
|
|
+ )
|
|
|
|
|
+ imageContainerTopToControlsConstraint = imageContainer.topAnchor.constraint(
|
|
|
|
|
+ equalTo: editControlsArea.bottomAnchor,
|
|
|
|
|
+ constant: 8
|
|
|
|
|
+ )
|
|
|
|
|
+ imageContainerMinHeightConstraint = imageContainer.heightAnchor.constraint(
|
|
|
|
|
+ greaterThanOrEqualToConstant: Self.editingImageMinHeight
|
|
|
|
|
+ )
|
|
|
|
|
+ imageContainerMinHeightConstraint?.priority = .defaultHigh
|
|
|
|
|
+ whiteningPanelHeightConstraint = whiteningPanel.heightAnchor.constraint(equalToConstant: 0)
|
|
|
|
|
+ saturationPanelHeightConstraint = saturationPanel.heightAnchor.constraint(equalToConstant: 0)
|
|
|
|
|
+ contrastPanelHeightConstraint = contrastPanel.heightAnchor.constraint(equalToConstant: 0)
|
|
|
|
|
+
|
|
|
NSLayoutConstraint.activate([
|
|
NSLayoutConstraint.activate([
|
|
|
backButton.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 24),
|
|
backButton.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 24),
|
|
|
backButton.topAnchor.constraint(equalTo: topAnchor, constant: 20),
|
|
backButton.topAnchor.constraint(equalTo: topAnchor, constant: 20),
|
|
@@ -227,21 +254,21 @@ final class PhotoPreviewOverlayView: NSView, AppearanceRefreshable {
|
|
|
|
|
|
|
|
previewSection.leadingAnchor.constraint(equalTo: leadingAnchor, constant: AppTheme.contentPanelInset),
|
|
previewSection.leadingAnchor.constraint(equalTo: leadingAnchor, constant: AppTheme.contentPanelInset),
|
|
|
previewSection.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -AppTheme.contentPanelInset),
|
|
previewSection.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -AppTheme.contentPanelInset),
|
|
|
- previewSection.topAnchor.constraint(equalTo: backButton.bottomAnchor, constant: 28),
|
|
|
|
|
- previewSection.bottomAnchor.constraint(equalTo: printButton.topAnchor, constant: -28),
|
|
|
|
|
|
|
+ previewSection.topAnchor.constraint(equalTo: backButton.bottomAnchor, constant: 24),
|
|
|
|
|
+ previewSection.bottomAnchor.constraint(equalTo: printButton.topAnchor, constant: -24),
|
|
|
|
|
|
|
|
- editControlsArea.leadingAnchor.constraint(equalTo: previewSection.leadingAnchor),
|
|
|
|
|
- editControlsArea.trailingAnchor.constraint(equalTo: previewSection.trailingAnchor),
|
|
|
|
|
- editControlsArea.topAnchor.constraint(equalTo: previewSection.topAnchor),
|
|
|
|
|
-
|
|
|
|
|
- editButton.trailingAnchor.constraint(equalTo: editControlsArea.trailingAnchor),
|
|
|
|
|
- editButton.topAnchor.constraint(equalTo: editControlsArea.topAnchor),
|
|
|
|
|
|
|
+ editButton.trailingAnchor.constraint(equalTo: previewSection.trailingAnchor),
|
|
|
|
|
+ editButton.topAnchor.constraint(equalTo: previewSection.topAnchor),
|
|
|
editButton.widthAnchor.constraint(equalToConstant: 40),
|
|
editButton.widthAnchor.constraint(equalToConstant: 40),
|
|
|
editButton.heightAnchor.constraint(equalToConstant: 40),
|
|
editButton.heightAnchor.constraint(equalToConstant: 40),
|
|
|
|
|
|
|
|
|
|
+ editControlsArea.leadingAnchor.constraint(equalTo: previewSection.leadingAnchor),
|
|
|
|
|
+ editControlsArea.trailingAnchor.constraint(equalTo: previewSection.trailingAnchor),
|
|
|
|
|
+ editControlsArea.topAnchor.constraint(equalTo: editButton.bottomAnchor, constant: 8),
|
|
|
|
|
+
|
|
|
editPanel.leadingAnchor.constraint(equalTo: editControlsArea.leadingAnchor),
|
|
editPanel.leadingAnchor.constraint(equalTo: editControlsArea.leadingAnchor),
|
|
|
editPanel.trailingAnchor.constraint(equalTo: editControlsArea.trailingAnchor),
|
|
editPanel.trailingAnchor.constraint(equalTo: editControlsArea.trailingAnchor),
|
|
|
- editPanel.topAnchor.constraint(equalTo: editButton.bottomAnchor, constant: 10),
|
|
|
|
|
|
|
+ editPanel.topAnchor.constraint(equalTo: editControlsArea.topAnchor),
|
|
|
|
|
|
|
|
whiteningPanel.leadingAnchor.constraint(equalTo: editControlsArea.leadingAnchor),
|
|
whiteningPanel.leadingAnchor.constraint(equalTo: editControlsArea.leadingAnchor),
|
|
|
whiteningPanel.trailingAnchor.constraint(equalTo: editControlsArea.trailingAnchor),
|
|
whiteningPanel.trailingAnchor.constraint(equalTo: editControlsArea.trailingAnchor),
|
|
@@ -255,17 +282,9 @@ final class PhotoPreviewOverlayView: NSView, AppearanceRefreshable {
|
|
|
contrastPanel.trailingAnchor.constraint(equalTo: editControlsArea.trailingAnchor),
|
|
contrastPanel.trailingAnchor.constraint(equalTo: editControlsArea.trailingAnchor),
|
|
|
contrastPanel.topAnchor.constraint(equalTo: editPanel.bottomAnchor, constant: 8),
|
|
contrastPanel.topAnchor.constraint(equalTo: editPanel.bottomAnchor, constant: 8),
|
|
|
|
|
|
|
|
- editControlsArea.bottomAnchor.constraint(greaterThanOrEqualTo: whiteningPanel.bottomAnchor),
|
|
|
|
|
- editControlsArea.bottomAnchor.constraint(greaterThanOrEqualTo: saturationPanel.bottomAnchor),
|
|
|
|
|
- editControlsArea.bottomAnchor.constraint(greaterThanOrEqualTo: contrastPanel.bottomAnchor),
|
|
|
|
|
- editControlsArea.bottomAnchor.constraint(greaterThanOrEqualTo: editPanel.bottomAnchor),
|
|
|
|
|
- editControlsArea.bottomAnchor.constraint(greaterThanOrEqualTo: editButton.bottomAnchor),
|
|
|
|
|
-
|
|
|
|
|
imageContainer.leadingAnchor.constraint(equalTo: previewSection.leadingAnchor),
|
|
imageContainer.leadingAnchor.constraint(equalTo: previewSection.leadingAnchor),
|
|
|
imageContainer.trailingAnchor.constraint(equalTo: previewSection.trailingAnchor),
|
|
imageContainer.trailingAnchor.constraint(equalTo: previewSection.trailingAnchor),
|
|
|
- imageContainer.heightAnchor.constraint(equalToConstant: 300),
|
|
|
|
|
- imageContainer.heightAnchor.constraint(lessThanOrEqualToConstant: 340),
|
|
|
|
|
- imageContainer.bottomAnchor.constraint(lessThanOrEqualTo: previewSection.bottomAnchor),
|
|
|
|
|
|
|
+ imageContainer.bottomAnchor.constraint(equalTo: previewSection.bottomAnchor),
|
|
|
|
|
|
|
|
imageView.leadingAnchor.constraint(equalTo: imageContainer.leadingAnchor, constant: 12),
|
|
imageView.leadingAnchor.constraint(equalTo: imageContainer.leadingAnchor, constant: 12),
|
|
|
imageView.trailingAnchor.constraint(equalTo: imageContainer.trailingAnchor, constant: -12),
|
|
imageView.trailingAnchor.constraint(equalTo: imageContainer.trailingAnchor, constant: -12),
|
|
@@ -275,13 +294,38 @@ final class PhotoPreviewOverlayView: NSView, AppearanceRefreshable {
|
|
|
printButton.centerXAnchor.constraint(equalTo: centerXAnchor),
|
|
printButton.centerXAnchor.constraint(equalTo: centerXAnchor),
|
|
|
printButton.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -28),
|
|
printButton.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -28),
|
|
|
printButton.widthAnchor.constraint(greaterThanOrEqualToConstant: 220),
|
|
printButton.widthAnchor.constraint(greaterThanOrEqualToConstant: 220),
|
|
|
|
|
+
|
|
|
|
|
+ whiteningPanelHeightConstraint!,
|
|
|
|
|
+ saturationPanelHeightConstraint!,
|
|
|
|
|
+ contrastPanelHeightConstraint!,
|
|
|
])
|
|
])
|
|
|
|
|
|
|
|
- updateImageContainerTopConstraint()
|
|
|
|
|
|
|
+ editControlsArea.isHidden = true
|
|
|
|
|
+ updatePreviewLayout()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private func updateImageContainerTopConstraint() {
|
|
|
|
|
- imageContainerTopConstraint?.isActive = false
|
|
|
|
|
|
|
+ private func updatePreviewLayout() {
|
|
|
|
|
+ let editing = isEditPanelVisible
|
|
|
|
|
+
|
|
|
|
|
+ editControlsArea.isHidden = !editing
|
|
|
|
|
+ imageContainerTopToPreviewConstraint?.isActive = !editing
|
|
|
|
|
+ imageContainerTopToControlsConstraint?.isActive = editing
|
|
|
|
|
+ imageContainerMinHeightConstraint?.isActive = editing
|
|
|
|
|
+
|
|
|
|
|
+ if editing {
|
|
|
|
|
+ updateEditControlsBottomConstraint()
|
|
|
|
|
+ } else {
|
|
|
|
|
+ editControlsBottomConstraint?.isActive = false
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ NSAnimationContext.runAnimationGroup { context in
|
|
|
|
|
+ context.duration = 0.2
|
|
|
|
|
+ layoutSubtreeIfNeeded()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private func updateEditControlsBottomConstraint() {
|
|
|
|
|
+ editControlsBottomConstraint?.isActive = false
|
|
|
|
|
|
|
|
let anchor: NSLayoutYAxisAnchor
|
|
let anchor: NSLayoutYAxisAnchor
|
|
|
if !whiteningPanel.isHidden {
|
|
if !whiteningPanel.isHidden {
|
|
@@ -290,20 +334,28 @@ final class PhotoPreviewOverlayView: NSView, AppearanceRefreshable {
|
|
|
anchor = saturationPanel.bottomAnchor
|
|
anchor = saturationPanel.bottomAnchor
|
|
|
} else if !contrastPanel.isHidden {
|
|
} else if !contrastPanel.isHidden {
|
|
|
anchor = contrastPanel.bottomAnchor
|
|
anchor = contrastPanel.bottomAnchor
|
|
|
- } else if !editPanel.isHidden {
|
|
|
|
|
- anchor = editPanel.bottomAnchor
|
|
|
|
|
} else {
|
|
} else {
|
|
|
- anchor = editButton.bottomAnchor
|
|
|
|
|
|
|
+ anchor = editPanel.bottomAnchor
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- imageContainerTopConstraint = imageContainer.topAnchor.constraint(equalTo: anchor, constant: 14)
|
|
|
|
|
- imageContainerTopConstraint.isActive = true
|
|
|
|
|
|
|
+ let bottomConstraint = editControlsArea.bottomAnchor.constraint(equalTo: anchor)
|
|
|
|
|
+ bottomConstraint.isActive = true
|
|
|
|
|
+ editControlsBottomConstraint = bottomConstraint
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private func hideAllAdjustmentPanels() {
|
|
private func hideAllAdjustmentPanels() {
|
|
|
- whiteningPanel.isHidden = true
|
|
|
|
|
- saturationPanel.isHidden = true
|
|
|
|
|
- contrastPanel.isHidden = true
|
|
|
|
|
|
|
+ setAdjustmentPanel(whiteningPanel, heightConstraint: whiteningPanelHeightConstraint, visible: false)
|
|
|
|
|
+ setAdjustmentPanel(saturationPanel, heightConstraint: saturationPanelHeightConstraint, visible: false)
|
|
|
|
|
+ setAdjustmentPanel(contrastPanel, heightConstraint: contrastPanelHeightConstraint, visible: false)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private func setAdjustmentPanel(
|
|
|
|
|
+ _ panel: PhotoAdjustSliderPanelView,
|
|
|
|
|
+ heightConstraint: NSLayoutConstraint?,
|
|
|
|
|
+ visible: Bool
|
|
|
|
|
+ ) {
|
|
|
|
|
+ panel.isHidden = !visible
|
|
|
|
|
+ heightConstraint?.constant = visible ? Self.adjustmentPanelHeight : 0
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private func toggleAdjustmentPanel(_ panel: PhotoAdjustSliderPanelView, action: PhotoEditAction, currentValue: Float) {
|
|
private func toggleAdjustmentPanel(_ panel: PhotoAdjustSliderPanelView, action: PhotoEditAction, currentValue: Float) {
|
|
@@ -314,18 +366,29 @@ final class PhotoPreviewOverlayView: NSView, AppearanceRefreshable {
|
|
|
editPanel.setActive(.contrast, isActive: false)
|
|
editPanel.setActive(.contrast, isActive: false)
|
|
|
|
|
|
|
|
if willShow {
|
|
if willShow {
|
|
|
- panel.isHidden = false
|
|
|
|
|
|
|
+ let heightConstraint: NSLayoutConstraint?
|
|
|
|
|
+ switch action {
|
|
|
|
|
+ case .whitening:
|
|
|
|
|
+ heightConstraint = whiteningPanelHeightConstraint
|
|
|
|
|
+ case .saturation:
|
|
|
|
|
+ heightConstraint = saturationPanelHeightConstraint
|
|
|
|
|
+ case .contrast:
|
|
|
|
|
+ heightConstraint = contrastPanelHeightConstraint
|
|
|
|
|
+ default:
|
|
|
|
|
+ heightConstraint = whiteningPanelHeightConstraint
|
|
|
|
|
+ }
|
|
|
|
|
+ setAdjustmentPanel(panel, heightConstraint: heightConstraint, visible: true)
|
|
|
panel.setValue(currentValue)
|
|
panel.setValue(currentValue)
|
|
|
editPanel.setActive(action, isActive: true)
|
|
editPanel.setActive(action, isActive: true)
|
|
|
}
|
|
}
|
|
|
- updateImageContainerTopConstraint()
|
|
|
|
|
|
|
+ updatePreviewLayout()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private func toggleEditPanel() {
|
|
private func toggleEditPanel() {
|
|
|
isEditPanelVisible.toggle()
|
|
isEditPanelVisible.toggle()
|
|
|
editPanel.isHidden = !isEditPanelVisible
|
|
editPanel.isHidden = !isEditPanelVisible
|
|
|
hideAllAdjustmentPanels()
|
|
hideAllAdjustmentPanels()
|
|
|
- updateImageContainerTopConstraint()
|
|
|
|
|
|
|
+ updatePreviewLayout()
|
|
|
if !isEditPanelVisible {
|
|
if !isEditPanelVisible {
|
|
|
exitCropMode()
|
|
exitCropMode()
|
|
|
}
|
|
}
|
|
@@ -345,7 +408,7 @@ final class PhotoPreviewOverlayView: NSView, AppearanceRefreshable {
|
|
|
toggleAdjustmentPanel(contrastPanel, action: .contrast, currentValue: contrastAmount)
|
|
toggleAdjustmentPanel(contrastPanel, action: .contrast, currentValue: contrastAmount)
|
|
|
case .crop:
|
|
case .crop:
|
|
|
hideAllAdjustmentPanels()
|
|
hideAllAdjustmentPanels()
|
|
|
- updateImageContainerTopConstraint()
|
|
|
|
|
|
|
+ updatePreviewLayout()
|
|
|
enterCropMode()
|
|
enterCropMode()
|
|
|
case .flip:
|
|
case .flip:
|
|
|
applyGeometricEdit { PhotoImageEditor.flipHorizontal($0) }
|
|
applyGeometricEdit { PhotoImageEditor.flipHorizontal($0) }
|
|
@@ -355,7 +418,7 @@ final class PhotoPreviewOverlayView: NSView, AppearanceRefreshable {
|
|
|
isEditPanelVisible = false
|
|
isEditPanelVisible = false
|
|
|
editPanel.isHidden = true
|
|
editPanel.isHidden = true
|
|
|
hideAllAdjustmentPanels()
|
|
hideAllAdjustmentPanels()
|
|
|
- updateImageContainerTopConstraint()
|
|
|
|
|
|
|
+ updatePreviewLayout()
|
|
|
exitCropMode()
|
|
exitCropMode()
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -381,7 +444,7 @@ final class PhotoPreviewOverlayView: NSView, AppearanceRefreshable {
|
|
|
isCropMode = true
|
|
isCropMode = true
|
|
|
editPanel.setActive(.crop, isActive: true)
|
|
editPanel.setActive(.crop, isActive: true)
|
|
|
hideAllAdjustmentPanels()
|
|
hideAllAdjustmentPanels()
|
|
|
- updateImageContainerTopConstraint()
|
|
|
|
|
|
|
+ updatePreviewLayout()
|
|
|
|
|
|
|
|
let overlay = PhotoCropOverlayView(imageView: imageView)
|
|
let overlay = PhotoCropOverlayView(imageView: imageView)
|
|
|
overlay.translatesAutoresizingMaskIntoConstraints = false
|
|
overlay.translatesAutoresizingMaskIntoConstraints = false
|
|
@@ -468,7 +531,7 @@ final class PhotoPreviewOverlayView: NSView, AppearanceRefreshable {
|
|
|
whiteningPanel.setValue(0)
|
|
whiteningPanel.setValue(0)
|
|
|
saturationPanel.setValue(1)
|
|
saturationPanel.setValue(1)
|
|
|
contrastPanel.setValue(1)
|
|
contrastPanel.setValue(1)
|
|
|
- updateImageContainerTopConstraint()
|
|
|
|
|
|
|
+ updatePreviewLayout()
|
|
|
exitCropMode()
|
|
exitCropMode()
|
|
|
|
|
|
|
|
imageView.image = originalImage
|
|
imageView.image = originalImage
|
|
@@ -737,8 +800,6 @@ private final class PhotoAdjustSliderPanelView: NSView, AppearanceRefreshable {
|
|
|
addSubview(slider)
|
|
addSubview(slider)
|
|
|
|
|
|
|
|
NSLayoutConstraint.activate([
|
|
NSLayoutConstraint.activate([
|
|
|
- heightAnchor.constraint(equalToConstant: 56),
|
|
|
|
|
-
|
|
|
|
|
titleLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 14),
|
|
titleLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 14),
|
|
|
titleLabel.centerYAnchor.constraint(equalTo: centerYAnchor),
|
|
titleLabel.centerYAnchor.constraint(equalTo: centerYAnchor),
|
|
|
titleLabel.widthAnchor.constraint(equalToConstant: 78),
|
|
titleLabel.widthAnchor.constraint(equalToConstant: 78),
|